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