xref: /cloud-hypervisor/pci/src/configuration.rs (revision 8803e4a2e7f8e9596b72f81d3c916390e5b10fbd)
1 // Copyright 2018 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-BSD-3-Clause file.
4 //
5 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
6 
7 use crate::device::BarReprogrammingParams;
8 use crate::{MsixConfig, PciInterruptPin};
9 use byteorder::{ByteOrder, LittleEndian};
10 use serde::{Deserialize, Serialize};
11 use std::fmt::{self, Display};
12 use std::sync::{Arc, Mutex};
13 use vm_device::PciBarType;
14 use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable};
15 
16 // The number of 32bit registers in the config space, 4096 bytes.
17 const NUM_CONFIGURATION_REGISTERS: usize = 1024;
18 
19 const STATUS_REG: usize = 1;
20 const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000;
21 const BAR0_REG: usize = 4;
22 const ROM_BAR_REG: usize = 12;
23 const ROM_BAR_IDX: usize = 6;
24 const BAR_IO_ADDR_MASK: u32 = 0xffff_fffc;
25 const BAR_MEM_ADDR_MASK: u32 = 0xffff_fff0;
26 const ROM_BAR_ADDR_MASK: u32 = 0xffff_f800;
27 const MSI_CAPABILITY_REGISTER_MASK: u32 = 0x0071_0000;
28 const MSIX_CAPABILITY_REGISTER_MASK: u32 = 0xc000_0000;
29 const NUM_BAR_REGS: usize = 6;
30 const CAPABILITY_LIST_HEAD_OFFSET: usize = 0x34;
31 const FIRST_CAPABILITY_OFFSET: usize = 0x40;
32 const CAPABILITY_MAX_OFFSET: usize = 192;
33 
34 const INTERRUPT_LINE_PIN_REG: usize = 15;
35 
36 pub const PCI_CONFIGURATION_ID: &str = "pci_configuration";
37 
38 /// Represents the types of PCI headers allowed in the configuration registers.
39 #[derive(Copy, Clone)]
40 pub enum PciHeaderType {
41     Device,
42     Bridge,
43 }
44 
45 /// Classes of PCI nodes.
46 #[allow(dead_code)]
47 #[derive(Copy, Clone)]
48 pub enum PciClassCode {
49     TooOld,
50     MassStorage,
51     NetworkController,
52     DisplayController,
53     MultimediaController,
54     MemoryController,
55     BridgeDevice,
56     SimpleCommunicationController,
57     BaseSystemPeripheral,
58     InputDevice,
59     DockingStation,
60     Processor,
61     SerialBusController,
62     WirelessController,
63     IntelligentIoController,
64     EncryptionController,
65     DataAcquisitionSignalProcessing,
66     Other = 0xff,
67 }
68 
69 impl PciClassCode {
70     pub fn get_register_value(self) -> u8 {
71         self as u8
72     }
73 }
74 
75 /// A PCI subclass. Each class in `PciClassCode` can specify a unique set of subclasses. This trait
76 /// is implemented by each subclass. It allows use of a trait object to generate configurations.
77 pub trait PciSubclass {
78     /// Convert this subclass to the value used in the PCI specification.
79     fn get_register_value(&self) -> u8;
80 }
81 
82 /// Subclasses of the MultimediaController class.
83 #[allow(dead_code)]
84 #[derive(Copy, Clone)]
85 pub enum PciMultimediaSubclass {
86     VideoController = 0x00,
87     AudioController = 0x01,
88     TelephonyDevice = 0x02,
89     AudioDevice = 0x03,
90     Other = 0x80,
91 }
92 
93 impl PciSubclass for PciMultimediaSubclass {
94     fn get_register_value(&self) -> u8 {
95         *self as u8
96     }
97 }
98 
99 /// Subclasses of the BridgeDevice
100 #[allow(dead_code)]
101 #[derive(Copy, Clone)]
102 pub enum PciBridgeSubclass {
103     HostBridge = 0x00,
104     IsaBridge = 0x01,
105     EisaBridge = 0x02,
106     McaBridge = 0x03,
107     PciToPciBridge = 0x04,
108     PcmciaBridge = 0x05,
109     NuBusBridge = 0x06,
110     CardBusBridge = 0x07,
111     RacEwayBridge = 0x08,
112     PciToPciSemiTransparentBridge = 0x09,
113     InfiniBrandToPciHostBridge = 0x0a,
114     OtherBridgeDevice = 0x80,
115 }
116 
117 impl PciSubclass for PciBridgeSubclass {
118     fn get_register_value(&self) -> u8 {
119         *self as u8
120     }
121 }
122 
123 /// Subclass of the SerialBus
124 #[allow(dead_code)]
125 #[derive(Copy, Clone)]
126 pub enum PciSerialBusSubClass {
127     Firewire = 0x00,
128     Accessbus = 0x01,
129     Ssa = 0x02,
130     Usb = 0x03,
131 }
132 
133 impl PciSubclass for PciSerialBusSubClass {
134     fn get_register_value(&self) -> u8 {
135         *self as u8
136     }
137 }
138 
139 /// Mass Storage Sub Classes
140 #[allow(dead_code)]
141 #[derive(Copy, Clone)]
142 pub enum PciMassStorageSubclass {
143     ScsiStorage = 0x00,
144     IdeInterface = 0x01,
145     FloppyController = 0x02,
146     IpiController = 0x03,
147     RaidController = 0x04,
148     AtaController = 0x05,
149     SataController = 0x06,
150     SerialScsiController = 0x07,
151     NvmController = 0x08,
152     MassStorage = 0x80,
153 }
154 
155 impl PciSubclass for PciMassStorageSubclass {
156     fn get_register_value(&self) -> u8 {
157         *self as u8
158     }
159 }
160 
161 /// Network Controller Sub Classes
162 #[allow(dead_code)]
163 #[derive(Copy, Clone)]
164 pub enum PciNetworkControllerSubclass {
165     EthernetController = 0x00,
166     TokenRingController = 0x01,
167     FddiController = 0x02,
168     AtmController = 0x03,
169     IsdnController = 0x04,
170     WorldFipController = 0x05,
171     PicmgController = 0x06,
172     InfinibandController = 0x07,
173     FabricController = 0x08,
174     NetworkController = 0x80,
175 }
176 
177 impl PciSubclass for PciNetworkControllerSubclass {
178     fn get_register_value(&self) -> u8 {
179         *self as u8
180     }
181 }
182 
183 /// A PCI class programming interface. Each combination of `PciClassCode` and
184 /// `PciSubclass` can specify a set of register-level programming interfaces.
185 /// This trait is implemented by each programming interface.
186 /// It allows use of a trait object to generate configurations.
187 pub trait PciProgrammingInterface {
188     /// Convert this programming interface to the value used in the PCI specification.
189     fn get_register_value(&self) -> u8;
190 }
191 
192 /// Types of PCI capabilities.
193 #[derive(PartialEq, Eq, Copy, Clone)]
194 #[allow(dead_code)]
195 #[allow(non_camel_case_types)]
196 #[repr(u8)]
197 pub enum PciCapabilityId {
198     ListId = 0,
199     PowerManagement = 0x01,
200     AcceleratedGraphicsPort = 0x02,
201     VitalProductData = 0x03,
202     SlotIdentification = 0x04,
203     MessageSignalledInterrupts = 0x05,
204     CompactPciHotSwap = 0x06,
205     PciX = 0x07,
206     HyperTransport = 0x08,
207     VendorSpecific = 0x09,
208     Debugport = 0x0A,
209     CompactPciCentralResourceControl = 0x0B,
210     PciStandardHotPlugController = 0x0C,
211     BridgeSubsystemVendorDeviceId = 0x0D,
212     AgpTargetPciPcibridge = 0x0E,
213     SecureDevice = 0x0F,
214     PciExpress = 0x10,
215     MsiX = 0x11,
216     SataDataIndexConf = 0x12,
217     PciAdvancedFeatures = 0x13,
218     PciEnhancedAllocation = 0x14,
219 }
220 
221 impl From<u8> for PciCapabilityId {
222     fn from(c: u8) -> Self {
223         match c {
224             0 => PciCapabilityId::ListId,
225             0x01 => PciCapabilityId::PowerManagement,
226             0x02 => PciCapabilityId::AcceleratedGraphicsPort,
227             0x03 => PciCapabilityId::VitalProductData,
228             0x04 => PciCapabilityId::SlotIdentification,
229             0x05 => PciCapabilityId::MessageSignalledInterrupts,
230             0x06 => PciCapabilityId::CompactPciHotSwap,
231             0x07 => PciCapabilityId::PciX,
232             0x08 => PciCapabilityId::HyperTransport,
233             0x09 => PciCapabilityId::VendorSpecific,
234             0x0A => PciCapabilityId::Debugport,
235             0x0B => PciCapabilityId::CompactPciCentralResourceControl,
236             0x0C => PciCapabilityId::PciStandardHotPlugController,
237             0x0D => PciCapabilityId::BridgeSubsystemVendorDeviceId,
238             0x0E => PciCapabilityId::AgpTargetPciPcibridge,
239             0x0F => PciCapabilityId::SecureDevice,
240             0x10 => PciCapabilityId::PciExpress,
241             0x11 => PciCapabilityId::MsiX,
242             0x12 => PciCapabilityId::SataDataIndexConf,
243             0x13 => PciCapabilityId::PciAdvancedFeatures,
244             0x14 => PciCapabilityId::PciEnhancedAllocation,
245             _ => PciCapabilityId::ListId,
246         }
247     }
248 }
249 
250 /// Types of PCI Express capabilities.
251 #[derive(PartialEq, Eq, Copy, Clone, Debug)]
252 #[allow(dead_code)]
253 #[repr(u16)]
254 pub enum PciExpressCapabilityId {
255     NullCapability = 0x0000,
256     AdvancedErrorReporting = 0x0001,
257     VirtualChannelMultiFunctionVirtualChannelNotPresent = 0x0002,
258     DeviceSerialNumber = 0x0003,
259     PowerBudgeting = 0x0004,
260     RootComplexLinkDeclaration = 0x0005,
261     RootComplexInternalLinkControl = 0x0006,
262     RootComplexEventCollectorEndpointAssociation = 0x0007,
263     MultiFunctionVirtualChannel = 0x0008,
264     VirtualChannelMultiFunctionVirtualChannelPresent = 0x0009,
265     RootComplexRegisterBlock = 0x000a,
266     VendorSpecificExtendedCapability = 0x000b,
267     ConfigurationAccessCorrelation = 0x000c,
268     AccessControlServices = 0x000d,
269     AlternativeRoutingIdentificationInterpretation = 0x000e,
270     AddressTranslationServices = 0x000f,
271     SingleRootIoVirtualization = 0x0010,
272     DeprecatedMultiRootIoVirtualization = 0x0011,
273     Multicast = 0x0012,
274     PageRequestInterface = 0x0013,
275     ReservedForAmd = 0x0014,
276     ResizeableBar = 0x0015,
277     DynamicPowerAllocation = 0x0016,
278     ThpRequester = 0x0017,
279     LatencyToleranceReporting = 0x0018,
280     SecondaryPciExpress = 0x0019,
281     ProtocolMultiplexing = 0x001a,
282     ProcessAddressSpaceId = 0x001b,
283     LnRequester = 0x001c,
284     DownstreamPortContainment = 0x001d,
285     L1PmSubstates = 0x001e,
286     PrecisionTimeMeasurement = 0x001f,
287     PciExpressOverMphy = 0x0020,
288     FRSQueueing = 0x0021,
289     ReadinessTimeReporting = 0x0022,
290     DesignatedVendorSpecificExtendedCapability = 0x0023,
291     VfResizeableBar = 0x0024,
292     DataLinkFeature = 0x0025,
293     PhysicalLayerSixteenGts = 0x0026,
294     LaneMarginingAtTheReceiver = 0x0027,
295     HierarchyId = 0x0028,
296     NativePcieEnclosureManagement = 0x0029,
297     PhysicalLayerThirtyTwoGts = 0x002a,
298     AlternateProtocol = 0x002b,
299     SystemFirmwareIntermediary = 0x002c,
300     ShadowFunctions = 0x002d,
301     DataObjectExchange = 0x002e,
302     Reserved = 0x002f,
303     ExtendedCapabilitiesAbsence = 0xffff,
304 }
305 
306 impl From<u16> for PciExpressCapabilityId {
307     fn from(c: u16) -> Self {
308         match c {
309             0x0000 => PciExpressCapabilityId::NullCapability,
310             0x0001 => PciExpressCapabilityId::AdvancedErrorReporting,
311             0x0002 => PciExpressCapabilityId::VirtualChannelMultiFunctionVirtualChannelNotPresent,
312             0x0003 => PciExpressCapabilityId::DeviceSerialNumber,
313             0x0004 => PciExpressCapabilityId::PowerBudgeting,
314             0x0005 => PciExpressCapabilityId::RootComplexLinkDeclaration,
315             0x0006 => PciExpressCapabilityId::RootComplexInternalLinkControl,
316             0x0007 => PciExpressCapabilityId::RootComplexEventCollectorEndpointAssociation,
317             0x0008 => PciExpressCapabilityId::MultiFunctionVirtualChannel,
318             0x0009 => PciExpressCapabilityId::VirtualChannelMultiFunctionVirtualChannelPresent,
319             0x000a => PciExpressCapabilityId::RootComplexRegisterBlock,
320             0x000b => PciExpressCapabilityId::VendorSpecificExtendedCapability,
321             0x000c => PciExpressCapabilityId::ConfigurationAccessCorrelation,
322             0x000d => PciExpressCapabilityId::AccessControlServices,
323             0x000e => PciExpressCapabilityId::AlternativeRoutingIdentificationInterpretation,
324             0x000f => PciExpressCapabilityId::AddressTranslationServices,
325             0x0010 => PciExpressCapabilityId::SingleRootIoVirtualization,
326             0x0011 => PciExpressCapabilityId::DeprecatedMultiRootIoVirtualization,
327             0x0012 => PciExpressCapabilityId::Multicast,
328             0x0013 => PciExpressCapabilityId::PageRequestInterface,
329             0x0014 => PciExpressCapabilityId::ReservedForAmd,
330             0x0015 => PciExpressCapabilityId::ResizeableBar,
331             0x0016 => PciExpressCapabilityId::DynamicPowerAllocation,
332             0x0017 => PciExpressCapabilityId::ThpRequester,
333             0x0018 => PciExpressCapabilityId::LatencyToleranceReporting,
334             0x0019 => PciExpressCapabilityId::SecondaryPciExpress,
335             0x001a => PciExpressCapabilityId::ProtocolMultiplexing,
336             0x001b => PciExpressCapabilityId::ProcessAddressSpaceId,
337             0x001c => PciExpressCapabilityId::LnRequester,
338             0x001d => PciExpressCapabilityId::DownstreamPortContainment,
339             0x001e => PciExpressCapabilityId::L1PmSubstates,
340             0x001f => PciExpressCapabilityId::PrecisionTimeMeasurement,
341             0x0020 => PciExpressCapabilityId::PciExpressOverMphy,
342             0x0021 => PciExpressCapabilityId::FRSQueueing,
343             0x0022 => PciExpressCapabilityId::ReadinessTimeReporting,
344             0x0023 => PciExpressCapabilityId::DesignatedVendorSpecificExtendedCapability,
345             0x0024 => PciExpressCapabilityId::VfResizeableBar,
346             0x0025 => PciExpressCapabilityId::DataLinkFeature,
347             0x0026 => PciExpressCapabilityId::PhysicalLayerSixteenGts,
348             0x0027 => PciExpressCapabilityId::LaneMarginingAtTheReceiver,
349             0x0028 => PciExpressCapabilityId::HierarchyId,
350             0x0029 => PciExpressCapabilityId::NativePcieEnclosureManagement,
351             0x002a => PciExpressCapabilityId::PhysicalLayerThirtyTwoGts,
352             0x002b => PciExpressCapabilityId::AlternateProtocol,
353             0x002c => PciExpressCapabilityId::SystemFirmwareIntermediary,
354             0x002d => PciExpressCapabilityId::ShadowFunctions,
355             0x002e => PciExpressCapabilityId::DataObjectExchange,
356             0xffff => PciExpressCapabilityId::ExtendedCapabilitiesAbsence,
357             _ => PciExpressCapabilityId::Reserved,
358         }
359     }
360 }
361 
362 /// A PCI capability list. Devices can optionally specify capabilities in their configuration space.
363 pub trait PciCapability {
364     fn bytes(&self) -> &[u8];
365     fn id(&self) -> PciCapabilityId;
366 }
367 
368 fn encode_32_bits_bar_size(bar_size: u32) -> Option<u32> {
369     if bar_size > 0 {
370         return Some(!(bar_size - 1));
371     }
372     None
373 }
374 
375 fn decode_32_bits_bar_size(bar_size: u32) -> Option<u32> {
376     if bar_size > 0 {
377         return Some(!bar_size + 1);
378     }
379     None
380 }
381 
382 fn encode_64_bits_bar_size(bar_size: u64) -> Option<(u32, u32)> {
383     if bar_size > 0 {
384         let result = !(bar_size - 1);
385         let result_hi = (result >> 32) as u32;
386         let result_lo = (result & 0xffff_ffff) as u32;
387         return Some((result_hi, result_lo));
388     }
389     None
390 }
391 
392 fn decode_64_bits_bar_size(bar_size_hi: u32, bar_size_lo: u32) -> Option<u64> {
393     let bar_size: u64 = ((bar_size_hi as u64) << 32) | (bar_size_lo as u64);
394     if bar_size > 0 {
395         return Some(!bar_size + 1);
396     }
397     None
398 }
399 
400 #[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
401 struct PciBar {
402     addr: u32,
403     size: u32,
404     used: bool,
405     r#type: Option<PciBarRegionType>,
406 }
407 
408 #[derive(Serialize, Deserialize)]
409 pub struct PciConfigurationState {
410     registers: Vec<u32>,
411     writable_bits: Vec<u32>,
412     bars: Vec<PciBar>,
413     rom_bar_addr: u32,
414     rom_bar_size: u32,
415     rom_bar_used: bool,
416     last_capability: Option<(usize, usize)>,
417     msix_cap_reg_idx: Option<usize>,
418 }
419 
420 /// Contains the configuration space of a PCI node.
421 /// See the [specification](https://en.wikipedia.org/wiki/PCI_configuration_space).
422 /// The configuration space is accessed with DWORD reads and writes from the guest.
423 pub struct PciConfiguration {
424     registers: [u32; NUM_CONFIGURATION_REGISTERS],
425     writable_bits: [u32; NUM_CONFIGURATION_REGISTERS], // writable bits for each register.
426     bars: [PciBar; NUM_BAR_REGS],
427     rom_bar_addr: u32,
428     rom_bar_size: u32,
429     rom_bar_used: bool,
430     // Contains the byte offset and size of the last capability.
431     last_capability: Option<(usize, usize)>,
432     msix_cap_reg_idx: Option<usize>,
433     msix_config: Option<Arc<Mutex<MsixConfig>>>,
434 }
435 
436 /// See pci_regs.h in kernel
437 #[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
438 pub enum PciBarRegionType {
439     Memory32BitRegion = 0,
440     IoRegion = 0x01,
441     Memory64BitRegion = 0x04,
442 }
443 
444 impl From<PciBarType> for PciBarRegionType {
445     fn from(type_: PciBarType) -> Self {
446         match type_ {
447             PciBarType::Io => PciBarRegionType::IoRegion,
448             PciBarType::Mmio32 => PciBarRegionType::Memory32BitRegion,
449             PciBarType::Mmio64 => PciBarRegionType::Memory64BitRegion,
450         }
451     }
452 }
453 
454 impl From<PciBarRegionType> for PciBarType {
455     fn from(val: PciBarRegionType) -> Self {
456         match val {
457             PciBarRegionType::IoRegion => PciBarType::Io,
458             PciBarRegionType::Memory32BitRegion => PciBarType::Mmio32,
459             PciBarRegionType::Memory64BitRegion => PciBarType::Mmio64,
460         }
461     }
462 }
463 
464 #[derive(Copy, Clone)]
465 pub enum PciBarPrefetchable {
466     NotPrefetchable = 0,
467     Prefetchable = 0x08,
468 }
469 
470 impl From<PciBarPrefetchable> for bool {
471     fn from(val: PciBarPrefetchable) -> Self {
472         match val {
473             PciBarPrefetchable::NotPrefetchable => false,
474             PciBarPrefetchable::Prefetchable => true,
475         }
476     }
477 }
478 
479 #[derive(Copy, Clone)]
480 pub struct PciBarConfiguration {
481     addr: u64,
482     size: u64,
483     idx: usize,
484     region_type: PciBarRegionType,
485     prefetchable: PciBarPrefetchable,
486 }
487 
488 #[derive(Debug)]
489 pub enum Error {
490     BarAddressInvalid(u64, u64),
491     BarInUse(usize),
492     BarInUse64(usize),
493     BarInvalid(usize),
494     BarInvalid64(usize),
495     BarSizeInvalid(u64),
496     CapabilityEmpty,
497     CapabilityLengthInvalid(usize),
498     CapabilitySpaceFull(usize),
499     Decode32BarSize,
500     Decode64BarSize,
501     Encode32BarSize,
502     Encode64BarSize,
503     RomBarAddressInvalid(u64, u64),
504     RomBarInUse(usize),
505     RomBarInvalid(usize),
506     RomBarSizeInvalid(u64),
507 }
508 pub type Result<T> = std::result::Result<T, Error>;
509 
510 impl std::error::Error for Error {}
511 
512 impl Display for Error {
513     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
514         use self::Error::*;
515         match self {
516             BarAddressInvalid(a, s) => write!(f, "address {a} size {s} too big"),
517             BarInUse(b) => write!(f, "bar {b} already used"),
518             BarInUse64(b) => write!(f, "64bit bar {b} already used(requires two regs)"),
519             BarInvalid(b) => write!(f, "bar {} invalid, max {}", b, NUM_BAR_REGS - 1),
520             BarInvalid64(b) => write!(
521                 f,
522                 "64bitbar {} invalid, requires two regs, max {}",
523                 b,
524                 NUM_BAR_REGS - 1
525             ),
526             BarSizeInvalid(s) => write!(f, "bar address {s} not a power of two"),
527             CapabilityEmpty => write!(f, "empty capabilities are invalid"),
528             CapabilityLengthInvalid(l) => write!(f, "Invalid capability length {l}"),
529             CapabilitySpaceFull(s) => write!(f, "capability of size {s} doesn't fit"),
530             Decode32BarSize => write!(f, "failed to decode 32 bits BAR size"),
531             Decode64BarSize => write!(f, "failed to decode 64 bits BAR size"),
532             Encode32BarSize => write!(f, "failed to encode 32 bits BAR size"),
533             Encode64BarSize => write!(f, "failed to encode 64 bits BAR size"),
534             RomBarAddressInvalid(a, s) => write!(f, "address {a} size {s} too big"),
535             RomBarInUse(b) => write!(f, "rom bar {b} already used"),
536             RomBarInvalid(b) => write!(f, "rom bar {} invalid, max {}", b, NUM_BAR_REGS - 1),
537             RomBarSizeInvalid(s) => write!(f, "rom bar address {s} not a power of two"),
538         }
539     }
540 }
541 
542 impl PciConfiguration {
543     #[allow(clippy::too_many_arguments)]
544     pub fn new(
545         vendor_id: u16,
546         device_id: u16,
547         revision_id: u8,
548         class_code: PciClassCode,
549         subclass: &dyn PciSubclass,
550         programming_interface: Option<&dyn PciProgrammingInterface>,
551         header_type: PciHeaderType,
552         subsystem_vendor_id: u16,
553         subsystem_id: u16,
554         msix_config: Option<Arc<Mutex<MsixConfig>>>,
555         state: Option<PciConfigurationState>,
556     ) -> Self {
557         let (
558             registers,
559             writable_bits,
560             bars,
561             rom_bar_addr,
562             rom_bar_size,
563             rom_bar_used,
564             last_capability,
565             msix_cap_reg_idx,
566         ) = if let Some(state) = state {
567             (
568                 state.registers.try_into().unwrap(),
569                 state.writable_bits.try_into().unwrap(),
570                 state.bars.try_into().unwrap(),
571                 state.rom_bar_addr,
572                 state.rom_bar_size,
573                 state.rom_bar_used,
574                 state.last_capability,
575                 state.msix_cap_reg_idx,
576             )
577         } else {
578             let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS];
579             let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS];
580             registers[0] = u32::from(device_id) << 16 | u32::from(vendor_id);
581             // TODO(dverkamp): Status should be write-1-to-clear
582             writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w)
583             let pi = if let Some(pi) = programming_interface {
584                 pi.get_register_value()
585             } else {
586                 0
587             };
588             registers[2] = u32::from(class_code.get_register_value()) << 24
589                 | u32::from(subclass.get_register_value()) << 16
590                 | u32::from(pi) << 8
591                 | u32::from(revision_id);
592             writable_bits[3] = 0x0000_00ff; // Cacheline size (r/w)
593             match header_type {
594                 PciHeaderType::Device => {
595                     registers[3] = 0x0000_0000; // Header type 0 (device)
596                     writable_bits[15] = 0x0000_00ff; // Interrupt line (r/w)
597                 }
598                 PciHeaderType::Bridge => {
599                     registers[3] = 0x0001_0000; // Header type 1 (bridge)
600                     writable_bits[9] = 0xfff0_fff0; // Memory base and limit
601                     writable_bits[15] = 0xffff_00ff; // Bridge control (r/w), interrupt line (r/w)
602                 }
603             };
604             registers[11] = u32::from(subsystem_id) << 16 | u32::from(subsystem_vendor_id);
605 
606             (
607                 registers,
608                 writable_bits,
609                 [PciBar::default(); NUM_BAR_REGS],
610                 0,
611                 0,
612                 false,
613                 None,
614                 None,
615             )
616         };
617 
618         PciConfiguration {
619             registers,
620             writable_bits,
621             bars,
622             rom_bar_addr,
623             rom_bar_size,
624             rom_bar_used,
625             last_capability,
626             msix_cap_reg_idx,
627             msix_config,
628         }
629     }
630 
631     fn state(&self) -> PciConfigurationState {
632         PciConfigurationState {
633             registers: self.registers.to_vec(),
634             writable_bits: self.writable_bits.to_vec(),
635             bars: self.bars.to_vec(),
636             rom_bar_addr: self.rom_bar_addr,
637             rom_bar_size: self.rom_bar_size,
638             rom_bar_used: self.rom_bar_used,
639             last_capability: self.last_capability,
640             msix_cap_reg_idx: self.msix_cap_reg_idx,
641         }
642     }
643 
644     /// Reads a 32bit register from `reg_idx` in the register map.
645     pub fn read_reg(&self, reg_idx: usize) -> u32 {
646         *(self.registers.get(reg_idx).unwrap_or(&0xffff_ffff))
647     }
648 
649     /// Writes a 32bit register to `reg_idx` in the register map.
650     pub fn write_reg(&mut self, reg_idx: usize, value: u32) {
651         let mut mask = self.writable_bits[reg_idx];
652 
653         if (BAR0_REG..BAR0_REG + NUM_BAR_REGS).contains(&reg_idx) {
654             // Handle very specific case where the BAR is being written with
655             // all 1's to retrieve the BAR size during next BAR reading.
656             if value == 0xffff_ffff {
657                 mask &= self.bars[reg_idx - 4].size;
658             }
659         } else if reg_idx == ROM_BAR_REG {
660             // Handle very specific case where the BAR is being written with
661             // all 1's on bits 31-11 to retrieve the BAR size during next BAR
662             // reading.
663             if value & ROM_BAR_ADDR_MASK == ROM_BAR_ADDR_MASK {
664                 mask &= self.rom_bar_size;
665             }
666         }
667 
668         if let Some(r) = self.registers.get_mut(reg_idx) {
669             *r = (*r & !self.writable_bits[reg_idx]) | (value & mask);
670         } else {
671             warn!("bad PCI register write {}", reg_idx);
672         }
673     }
674 
675     /// Writes a 16bit word to `offset`. `offset` must be 16bit aligned.
676     pub fn write_word(&mut self, offset: usize, value: u16) {
677         let shift = match offset % 4 {
678             0 => 0,
679             2 => 16,
680             _ => {
681                 warn!("bad PCI config write offset {}", offset);
682                 return;
683             }
684         };
685         let reg_idx = offset / 4;
686 
687         if let Some(r) = self.registers.get_mut(reg_idx) {
688             let writable_mask = self.writable_bits[reg_idx];
689             let mask = (0xffffu32 << shift) & writable_mask;
690             let shifted_value = (u32::from(value) << shift) & writable_mask;
691             *r = *r & !mask | shifted_value;
692         } else {
693             warn!("bad PCI config write offset {}", offset);
694         }
695     }
696 
697     /// Writes a byte to `offset`.
698     pub fn write_byte(&mut self, offset: usize, value: u8) {
699         self.write_byte_internal(offset, value, true);
700     }
701 
702     /// Writes a byte to `offset`, optionally enforcing read-only bits.
703     fn write_byte_internal(&mut self, offset: usize, value: u8, apply_writable_mask: bool) {
704         let shift = (offset % 4) * 8;
705         let reg_idx = offset / 4;
706 
707         if let Some(r) = self.registers.get_mut(reg_idx) {
708             let writable_mask = if apply_writable_mask {
709                 self.writable_bits[reg_idx]
710             } else {
711                 0xffff_ffff
712             };
713             let mask = (0xffu32 << shift) & writable_mask;
714             let shifted_value = (u32::from(value) << shift) & writable_mask;
715             *r = *r & !mask | shifted_value;
716         } else {
717             warn!("bad PCI config write offset {}", offset);
718         }
719     }
720 
721     /// Adds a region specified by `config`.  Configures the specified BAR(s) to
722     /// report this region and size to the guest kernel.  Enforces a few constraints
723     /// (i.e, region size must be power of two, register not already used).
724     pub fn add_pci_bar(&mut self, config: &PciBarConfiguration) -> Result<()> {
725         let bar_idx = config.idx;
726         let reg_idx = BAR0_REG + bar_idx;
727 
728         if self.bars[bar_idx].used {
729             return Err(Error::BarInUse(bar_idx));
730         }
731 
732         if config.size.count_ones() != 1 {
733             return Err(Error::BarSizeInvalid(config.size));
734         }
735 
736         if bar_idx >= NUM_BAR_REGS {
737             return Err(Error::BarInvalid(bar_idx));
738         }
739 
740         let end_addr = config
741             .addr
742             .checked_add(config.size - 1)
743             .ok_or(Error::BarAddressInvalid(config.addr, config.size))?;
744         match config.region_type {
745             PciBarRegionType::Memory32BitRegion | PciBarRegionType::IoRegion => {
746                 if end_addr > u64::from(u32::MAX) {
747                     return Err(Error::BarAddressInvalid(config.addr, config.size));
748                 }
749 
750                 // Encode the BAR size as expected by the software running in
751                 // the guest.
752                 self.bars[bar_idx].size =
753                     encode_32_bits_bar_size(config.size as u32).ok_or(Error::Encode32BarSize)?;
754             }
755             PciBarRegionType::Memory64BitRegion => {
756                 if bar_idx + 1 >= NUM_BAR_REGS {
757                     return Err(Error::BarInvalid64(bar_idx));
758                 }
759 
760                 if self.bars[bar_idx + 1].used {
761                     return Err(Error::BarInUse64(bar_idx));
762                 }
763 
764                 // Encode the BAR size as expected by the software running in
765                 // the guest.
766                 let (bar_size_hi, bar_size_lo) =
767                     encode_64_bits_bar_size(config.size).ok_or(Error::Encode64BarSize)?;
768 
769                 self.registers[reg_idx + 1] = (config.addr >> 32) as u32;
770                 self.writable_bits[reg_idx + 1] = 0xffff_ffff;
771                 self.bars[bar_idx + 1].addr = self.registers[reg_idx + 1];
772                 self.bars[bar_idx].size = bar_size_lo;
773                 self.bars[bar_idx + 1].size = bar_size_hi;
774                 self.bars[bar_idx + 1].used = true;
775             }
776         }
777 
778         let (mask, lower_bits) = match config.region_type {
779             PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => (
780                 BAR_MEM_ADDR_MASK,
781                 config.prefetchable as u32 | config.region_type as u32,
782             ),
783             PciBarRegionType::IoRegion => (BAR_IO_ADDR_MASK, config.region_type as u32),
784         };
785 
786         self.registers[reg_idx] = ((config.addr as u32) & mask) | lower_bits;
787         self.writable_bits[reg_idx] = mask;
788         self.bars[bar_idx].addr = self.registers[reg_idx];
789         self.bars[bar_idx].used = true;
790         self.bars[bar_idx].r#type = Some(config.region_type);
791 
792         Ok(())
793     }
794 
795     /// Adds rom expansion BAR.
796     pub fn add_pci_rom_bar(&mut self, config: &PciBarConfiguration, active: u32) -> Result<()> {
797         let bar_idx = config.idx;
798         let reg_idx = ROM_BAR_REG;
799 
800         if self.rom_bar_used {
801             return Err(Error::RomBarInUse(bar_idx));
802         }
803 
804         if config.size.count_ones() != 1 {
805             return Err(Error::RomBarSizeInvalid(config.size));
806         }
807 
808         if bar_idx != ROM_BAR_IDX {
809             return Err(Error::RomBarInvalid(bar_idx));
810         }
811 
812         let end_addr = config
813             .addr
814             .checked_add(config.size - 1)
815             .ok_or(Error::RomBarAddressInvalid(config.addr, config.size))?;
816 
817         if end_addr > u64::from(u32::MAX) {
818             return Err(Error::RomBarAddressInvalid(config.addr, config.size));
819         }
820 
821         self.registers[reg_idx] = (config.addr as u32) | active;
822         self.writable_bits[reg_idx] = ROM_BAR_ADDR_MASK;
823         self.rom_bar_addr = self.registers[reg_idx];
824         self.rom_bar_size =
825             encode_32_bits_bar_size(config.size as u32).ok_or(Error::Encode32BarSize)?;
826         self.rom_bar_used = true;
827 
828         Ok(())
829     }
830 
831     /// Returns the address of the given BAR region.
832     pub fn get_bar_addr(&self, bar_num: usize) -> u64 {
833         let bar_idx = BAR0_REG + bar_num;
834 
835         let mut addr = u64::from(self.bars[bar_num].addr & self.writable_bits[bar_idx]);
836 
837         if let Some(bar_type) = self.bars[bar_num].r#type {
838             if bar_type == PciBarRegionType::Memory64BitRegion {
839                 addr |= u64::from(self.bars[bar_num + 1].addr) << 32;
840             }
841         }
842 
843         addr
844     }
845 
846     /// Configures the IRQ line and pin used by this device.
847     pub fn set_irq(&mut self, line: u8, pin: PciInterruptPin) {
848         // `pin` is 1-based in the pci config space.
849         let pin_idx = (pin as u32) + 1;
850         self.registers[INTERRUPT_LINE_PIN_REG] = (self.registers[INTERRUPT_LINE_PIN_REG]
851             & 0xffff_0000)
852             | (pin_idx << 8)
853             | u32::from(line);
854     }
855 
856     /// Adds the capability `cap_data` to the list of capabilities.
857     /// `cap_data` should include the two-byte PCI capability header (type, next),
858     /// but not populate it. Correct values will be generated automatically based
859     /// on `cap_data.id()`.
860     pub fn add_capability(&mut self, cap_data: &dyn PciCapability) -> Result<usize> {
861         let total_len = cap_data.bytes().len();
862         // Check that the length is valid.
863         if cap_data.bytes().is_empty() {
864             return Err(Error::CapabilityEmpty);
865         }
866         let (cap_offset, tail_offset) = match self.last_capability {
867             Some((offset, len)) => (Self::next_dword(offset, len), offset + 1),
868             None => (FIRST_CAPABILITY_OFFSET, CAPABILITY_LIST_HEAD_OFFSET),
869         };
870         let end_offset = cap_offset
871             .checked_add(total_len)
872             .ok_or(Error::CapabilitySpaceFull(total_len))?;
873         if end_offset > CAPABILITY_MAX_OFFSET {
874             return Err(Error::CapabilitySpaceFull(total_len));
875         }
876         self.registers[STATUS_REG] |= STATUS_REG_CAPABILITIES_USED_MASK;
877         self.write_byte_internal(tail_offset, cap_offset as u8, false);
878         self.write_byte_internal(cap_offset, cap_data.id() as u8, false);
879         self.write_byte_internal(cap_offset + 1, 0, false); // Next pointer.
880         for (i, byte) in cap_data.bytes().iter().enumerate() {
881             self.write_byte_internal(cap_offset + i + 2, *byte, false);
882         }
883         self.last_capability = Some((cap_offset, total_len));
884 
885         match cap_data.id() {
886             PciCapabilityId::MessageSignalledInterrupts => {
887                 self.writable_bits[cap_offset / 4] = MSI_CAPABILITY_REGISTER_MASK;
888             }
889             PciCapabilityId::MsiX => {
890                 self.msix_cap_reg_idx = Some(cap_offset / 4);
891                 self.writable_bits[self.msix_cap_reg_idx.unwrap()] = MSIX_CAPABILITY_REGISTER_MASK;
892             }
893             _ => {}
894         }
895 
896         Ok(cap_offset)
897     }
898 
899     // Find the next aligned offset after the one given.
900     fn next_dword(offset: usize, len: usize) -> usize {
901         let next = offset + len;
902         (next + 3) & !3
903     }
904 
905     pub fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
906         if offset as usize + data.len() > 4 {
907             return;
908         }
909 
910         // Handle potential write to MSI-X message control register
911         if let Some(msix_cap_reg_idx) = self.msix_cap_reg_idx {
912             if let Some(msix_config) = &self.msix_config {
913                 if msix_cap_reg_idx == reg_idx && offset == 2 && data.len() == 2 {
914                     msix_config
915                         .lock()
916                         .unwrap()
917                         .set_msg_ctl(LittleEndian::read_u16(data));
918                 } else if msix_cap_reg_idx == reg_idx && offset == 0 && data.len() == 4 {
919                     msix_config
920                         .lock()
921                         .unwrap()
922                         .set_msg_ctl((LittleEndian::read_u32(data) >> 16) as u16);
923                 }
924             }
925         }
926 
927         match data.len() {
928             1 => self.write_byte(reg_idx * 4 + offset as usize, data[0]),
929             2 => self.write_word(
930                 reg_idx * 4 + offset as usize,
931                 u16::from(data[0]) | u16::from(data[1]) << 8,
932             ),
933             4 => self.write_reg(reg_idx, LittleEndian::read_u32(data)),
934             _ => (),
935         }
936     }
937 
938     pub fn read_config_register(&self, reg_idx: usize) -> u32 {
939         self.read_reg(reg_idx)
940     }
941 
942     pub fn detect_bar_reprogramming(
943         &mut self,
944         reg_idx: usize,
945         data: &[u8],
946     ) -> Option<BarReprogrammingParams> {
947         if data.len() != 4 {
948             return None;
949         }
950 
951         let value = LittleEndian::read_u32(data);
952 
953         let mask = self.writable_bits[reg_idx];
954         if (BAR0_REG..BAR0_REG + NUM_BAR_REGS).contains(&reg_idx) {
955             // Ignore the case where the BAR size is being asked for.
956             if value == 0xffff_ffff {
957                 return None;
958             }
959 
960             let bar_idx = reg_idx - 4;
961             // Handle special case where the address being written is
962             // different from the address initially provided. This is a
963             // BAR reprogramming case which needs to be properly caught.
964             if let Some(bar_type) = self.bars[bar_idx].r#type {
965                 // In case of 64 bits memory BAR, we don't do anything until
966                 // the upper BAR is modified, otherwise we would be moving the
967                 // BAR to a wrong location in memory.
968                 if bar_type == PciBarRegionType::Memory64BitRegion {
969                     return None;
970                 }
971 
972                 // Ignore the case where the value is unchanged.
973                 if (value & mask) == (self.bars[bar_idx].addr & mask) {
974                     return None;
975                 }
976 
977                 info!(
978                     "Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
979                     reg_idx, self.registers[reg_idx], value
980                 );
981                 let old_base = u64::from(self.bars[bar_idx].addr & mask);
982                 let new_base = u64::from(value & mask);
983                 let len = u64::from(
984                     decode_32_bits_bar_size(self.bars[bar_idx].size)
985                         .ok_or(Error::Decode32BarSize)
986                         .unwrap(),
987                 );
988                 let region_type = bar_type;
989 
990                 self.bars[bar_idx].addr = value;
991 
992                 return Some(BarReprogrammingParams {
993                     old_base,
994                     new_base,
995                     len,
996                     region_type,
997                 });
998             } else if (reg_idx > BAR0_REG)
999                 && ((self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1])
1000                     != (self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1])
1001                     || (value & mask) != (self.bars[bar_idx].addr & mask))
1002             {
1003                 info!(
1004                     "Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
1005                     reg_idx, self.registers[reg_idx], value
1006                 );
1007                 let old_base = u64::from(self.bars[bar_idx].addr & mask) << 32
1008                     | u64::from(self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1]);
1009                 let new_base = u64::from(value & mask) << 32
1010                     | u64::from(self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1]);
1011                 let len =
1012                     decode_64_bits_bar_size(self.bars[bar_idx].size, self.bars[bar_idx - 1].size)
1013                         .ok_or(Error::Decode64BarSize)
1014                         .unwrap();
1015                 let region_type = PciBarRegionType::Memory64BitRegion;
1016 
1017                 self.bars[bar_idx].addr = value;
1018                 self.bars[bar_idx - 1].addr = self.registers[reg_idx - 1];
1019 
1020                 return Some(BarReprogrammingParams {
1021                     old_base,
1022                     new_base,
1023                     len,
1024                     region_type,
1025                 });
1026             }
1027         } else if reg_idx == ROM_BAR_REG && (value & mask) != (self.rom_bar_addr & mask) {
1028             // Ignore the case where the BAR size is being asked for.
1029             if value & ROM_BAR_ADDR_MASK == ROM_BAR_ADDR_MASK {
1030                 return None;
1031             }
1032 
1033             info!(
1034                 "Detected ROM BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
1035                 reg_idx, self.registers[reg_idx], value
1036             );
1037             let old_base = u64::from(self.rom_bar_addr & mask);
1038             let new_base = u64::from(value & mask);
1039             let len = u64::from(
1040                 decode_32_bits_bar_size(self.rom_bar_size)
1041                     .ok_or(Error::Decode32BarSize)
1042                     .unwrap(),
1043             );
1044             let region_type = PciBarRegionType::Memory32BitRegion;
1045 
1046             self.rom_bar_addr = value;
1047 
1048             return Some(BarReprogrammingParams {
1049                 old_base,
1050                 new_base,
1051                 len,
1052                 region_type,
1053             });
1054         }
1055 
1056         None
1057     }
1058 }
1059 
1060 impl Pausable for PciConfiguration {}
1061 
1062 impl Snapshottable for PciConfiguration {
1063     fn id(&self) -> String {
1064         String::from(PCI_CONFIGURATION_ID)
1065     }
1066 
1067     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
1068         Snapshot::new_from_state(&self.state())
1069     }
1070 }
1071 
1072 impl Default for PciBarConfiguration {
1073     fn default() -> Self {
1074         PciBarConfiguration {
1075             idx: 0,
1076             addr: 0,
1077             size: 0,
1078             region_type: PciBarRegionType::Memory64BitRegion,
1079             prefetchable: PciBarPrefetchable::NotPrefetchable,
1080         }
1081     }
1082 }
1083 
1084 impl PciBarConfiguration {
1085     pub fn new(
1086         idx: usize,
1087         size: u64,
1088         region_type: PciBarRegionType,
1089         prefetchable: PciBarPrefetchable,
1090     ) -> Self {
1091         PciBarConfiguration {
1092             idx,
1093             addr: 0,
1094             size,
1095             region_type,
1096             prefetchable,
1097         }
1098     }
1099 
1100     #[must_use]
1101     pub fn set_index(mut self, idx: usize) -> Self {
1102         self.idx = idx;
1103         self
1104     }
1105 
1106     #[must_use]
1107     pub fn set_address(mut self, addr: u64) -> Self {
1108         self.addr = addr;
1109         self
1110     }
1111 
1112     #[must_use]
1113     pub fn set_size(mut self, size: u64) -> Self {
1114         self.size = size;
1115         self
1116     }
1117 
1118     #[must_use]
1119     pub fn set_region_type(mut self, region_type: PciBarRegionType) -> Self {
1120         self.region_type = region_type;
1121         self
1122     }
1123 
1124     #[must_use]
1125     pub fn set_prefetchable(mut self, prefetchable: PciBarPrefetchable) -> Self {
1126         self.prefetchable = prefetchable;
1127         self
1128     }
1129 
1130     pub fn idx(&self) -> usize {
1131         self.idx
1132     }
1133 
1134     pub fn addr(&self) -> u64 {
1135         self.addr
1136     }
1137 
1138     pub fn size(&self) -> u64 {
1139         self.size
1140     }
1141 
1142     pub fn region_type(&self) -> PciBarRegionType {
1143         self.region_type
1144     }
1145 
1146     pub fn prefetchable(&self) -> PciBarPrefetchable {
1147         self.prefetchable
1148     }
1149 }
1150 
1151 #[cfg(test)]
1152 mod tests {
1153     use vm_memory::ByteValued;
1154 
1155     use super::*;
1156 
1157     #[repr(packed)]
1158     #[derive(Clone, Copy, Default)]
1159     #[allow(dead_code)]
1160     struct TestCap {
1161         len: u8,
1162         foo: u8,
1163     }
1164 
1165     // SAFETY: All members are simple numbers and any value is valid.
1166     unsafe impl ByteValued for TestCap {}
1167 
1168     impl PciCapability for TestCap {
1169         fn bytes(&self) -> &[u8] {
1170             self.as_slice()
1171         }
1172 
1173         fn id(&self) -> PciCapabilityId {
1174             PciCapabilityId::VendorSpecific
1175         }
1176     }
1177 
1178     #[test]
1179     fn add_capability() {
1180         let mut cfg = PciConfiguration::new(
1181             0x1234,
1182             0x5678,
1183             0x1,
1184             PciClassCode::MultimediaController,
1185             &PciMultimediaSubclass::AudioController,
1186             None,
1187             PciHeaderType::Device,
1188             0xABCD,
1189             0x2468,
1190             None,
1191             None,
1192         );
1193 
1194         // Add two capabilities with different contents.
1195         let cap1 = TestCap { len: 4, foo: 0xAA };
1196         let cap1_offset = cfg.add_capability(&cap1).unwrap();
1197         assert_eq!(cap1_offset % 4, 0);
1198 
1199         let cap2 = TestCap {
1200             len: 0x04,
1201             foo: 0x55,
1202         };
1203         let cap2_offset = cfg.add_capability(&cap2).unwrap();
1204         assert_eq!(cap2_offset % 4, 0);
1205 
1206         // The capability list head should be pointing to cap1.
1207         let cap_ptr = cfg.read_reg(CAPABILITY_LIST_HEAD_OFFSET / 4) & 0xFF;
1208         assert_eq!(cap1_offset, cap_ptr as usize);
1209 
1210         // Verify the contents of the capabilities.
1211         let cap1_data = cfg.read_reg(cap1_offset / 4);
1212         assert_eq!(cap1_data & 0xFF, 0x09); // capability ID
1213         assert_eq!((cap1_data >> 8) & 0xFF, cap2_offset as u32); // next capability pointer
1214         assert_eq!((cap1_data >> 16) & 0xFF, 0x04); // cap1.len
1215         assert_eq!((cap1_data >> 24) & 0xFF, 0xAA); // cap1.foo
1216 
1217         let cap2_data = cfg.read_reg(cap2_offset / 4);
1218         assert_eq!(cap2_data & 0xFF, 0x09); // capability ID
1219         assert_eq!((cap2_data >> 8) & 0xFF, 0x00); // next capability pointer
1220         assert_eq!((cap2_data >> 16) & 0xFF, 0x04); // cap2.len
1221         assert_eq!((cap2_data >> 24) & 0xFF, 0x55); // cap2.foo
1222     }
1223 
1224     #[derive(Copy, Clone)]
1225     enum TestPi {
1226         Test = 0x5a,
1227     }
1228 
1229     impl PciProgrammingInterface for TestPi {
1230         fn get_register_value(&self) -> u8 {
1231             *self as u8
1232         }
1233     }
1234 
1235     #[test]
1236     fn class_code() {
1237         let cfg = PciConfiguration::new(
1238             0x1234,
1239             0x5678,
1240             0x1,
1241             PciClassCode::MultimediaController,
1242             &PciMultimediaSubclass::AudioController,
1243             Some(&TestPi::Test),
1244             PciHeaderType::Device,
1245             0xABCD,
1246             0x2468,
1247             None,
1248             None,
1249         );
1250 
1251         let class_reg = cfg.read_reg(2);
1252         let class_code = (class_reg >> 24) & 0xFF;
1253         let subclass = (class_reg >> 16) & 0xFF;
1254         let prog_if = (class_reg >> 8) & 0xFF;
1255         assert_eq!(class_code, 0x04);
1256         assert_eq!(subclass, 0x01);
1257         assert_eq!(prog_if, 0x5a);
1258     }
1259 }
1260