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