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