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