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