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