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