1 //! Module for dealing with a PCI bus in general, without anything specific to VirtIO. 2 3 use bitflags::bitflags; 4 use core::{ 5 array, 6 convert::TryFrom, 7 fmt::{self, Display, Formatter}, 8 }; 9 use log::warn; 10 use thiserror::Error; 11 12 const INVALID_READ: u32 = 0xffffffff; 13 14 /// The maximum number of devices on a bus. 15 const MAX_DEVICES: u8 = 32; 16 /// The maximum number of functions on a device. 17 const MAX_FUNCTIONS: u8 = 8; 18 19 /// The offset in bytes to the status and command fields within PCI configuration space. 20 const STATUS_COMMAND_OFFSET: u8 = 0x04; 21 /// The offset in bytes to BAR0 within PCI configuration space. 22 const BAR0_OFFSET: u8 = 0x10; 23 24 /// ID for vendor-specific PCI capabilities. 25 pub const PCI_CAP_ID_VNDR: u8 = 0x09; 26 27 bitflags! { 28 /// The status register in PCI configuration space. 29 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] 30 pub struct Status: u16 { 31 // Bits 0-2 are reserved. 32 /// The state of the device's INTx# signal. 33 const INTERRUPT_STATUS = 1 << 3; 34 /// The device has a linked list of capabilities. 35 const CAPABILITIES_LIST = 1 << 4; 36 /// The device is capabile of running at 66 MHz rather than 33 MHz. 37 const MHZ_66_CAPABLE = 1 << 5; 38 // Bit 6 is reserved. 39 /// The device can accept fast back-to-back transactions not from the same agent. 40 const FAST_BACK_TO_BACK_CAPABLE = 1 << 7; 41 /// The bus agent observed a parity error (if parity error handling is enabled). 42 const MASTER_DATA_PARITY_ERROR = 1 << 8; 43 // Bits 9-10 are DEVSEL timing. 44 /// A target device terminated a transaction with target-abort. 45 const SIGNALED_TARGET_ABORT = 1 << 11; 46 /// A master device transaction was terminated with target-abort. 47 const RECEIVED_TARGET_ABORT = 1 << 12; 48 /// A master device transaction was terminated with master-abort. 49 const RECEIVED_MASTER_ABORT = 1 << 13; 50 /// A device asserts SERR#. 51 const SIGNALED_SYSTEM_ERROR = 1 << 14; 52 /// The device detects a parity error, even if parity error handling is disabled. 53 const DETECTED_PARITY_ERROR = 1 << 15; 54 } 55 } 56 57 bitflags! { 58 /// The command register in PCI configuration space. 59 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] 60 pub struct Command: u16 { 61 /// The device can respond to I/O Space accesses. 62 const IO_SPACE = 1 << 0; 63 /// The device can respond to Memory Space accesses. 64 const MEMORY_SPACE = 1 << 1; 65 /// The device can behave as a bus master. 66 const BUS_MASTER = 1 << 2; 67 /// The device can monitor Special Cycle operations. 68 const SPECIAL_CYCLES = 1 << 3; 69 /// The device can generate the Memory Write and Invalidate command. 70 const MEMORY_WRITE_AND_INVALIDATE_ENABLE = 1 << 4; 71 /// The device will snoop palette register data. 72 const VGA_PALETTE_SNOOP = 1 << 5; 73 /// The device should take its normal action when a parity error is detected. 74 const PARITY_ERROR_RESPONSE = 1 << 6; 75 // Bit 7 is reserved. 76 /// The SERR# driver is enabled. 77 const SERR_ENABLE = 1 << 8; 78 /// The device is allowed to generate fast back-to-back transactions. 79 const FAST_BACK_TO_BACK_ENABLE = 1 << 9; 80 /// Assertion of the device's INTx# signal is disabled. 81 const INTERRUPT_DISABLE = 1 << 10; 82 } 83 } 84 85 /// Errors accessing a PCI device. 86 #[derive(Copy, Clone, Debug, Eq, Error, PartialEq)] 87 pub enum PciError { 88 /// The device reported an invalid BAR type. 89 #[error("Invalid PCI BAR type")] 90 InvalidBarType, 91 } 92 93 /// The root complex of a PCI bus. 94 #[derive(Debug)] 95 pub struct PciRoot<C: ConfigurationAccess> { 96 pub(crate) configuration_access: C, 97 } 98 99 /// A PCI Configuration Access Mechanism. 100 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 101 pub enum Cam { 102 /// The PCI memory-mapped Configuration Access Mechanism. 103 /// 104 /// This provides access to 256 bytes of configuration space per device function. 105 MmioCam, 106 /// The PCIe memory-mapped Enhanced Configuration Access Mechanism. 107 /// 108 /// This provides access to 4 KiB of configuration space per device function. 109 Ecam, 110 } 111 112 impl Cam { 113 /// Returns the total size in bytes of the memory-mapped region. size(self) -> u32114 pub const fn size(self) -> u32 { 115 match self { 116 Self::MmioCam => 0x1000000, 117 Self::Ecam => 0x10000000, 118 } 119 } 120 121 /// Returns the offset in bytes within the CAM region for the given device, function and 122 /// register. cam_offset(self, device_function: DeviceFunction, register_offset: u8) -> u32123 pub fn cam_offset(self, device_function: DeviceFunction, register_offset: u8) -> u32 { 124 assert!(device_function.valid()); 125 126 let bdf = (device_function.bus as u32) << 8 127 | (device_function.device as u32) << 3 128 | device_function.function as u32; 129 let address = 130 bdf << match self { 131 Cam::MmioCam => 8, 132 Cam::Ecam => 12, 133 } | register_offset as u32; 134 // Ensure that address is within range. 135 assert!(address < self.size()); 136 // Ensure that address is word-aligned. 137 assert!(address & 0x3 == 0); 138 address 139 } 140 } 141 142 impl<C: ConfigurationAccess> PciRoot<C> { 143 /// Creates a new `PciRoot` to access a PCI root complex through the given configuration access 144 /// implementation. new(configuration_access: C) -> Self145 pub fn new(configuration_access: C) -> Self { 146 Self { 147 configuration_access, 148 } 149 } 150 151 /// Enumerates PCI devices on the given bus. enumerate_bus(&self, bus: u8) -> BusDeviceIterator<C>152 pub fn enumerate_bus(&self, bus: u8) -> BusDeviceIterator<C> { 153 // Safe because the BusDeviceIterator only reads read-only fields. 154 let configuration_access = unsafe { self.configuration_access.unsafe_clone() }; 155 BusDeviceIterator { 156 configuration_access, 157 next: DeviceFunction { 158 bus, 159 device: 0, 160 function: 0, 161 }, 162 } 163 } 164 165 /// Reads the status and command registers of the given device function. get_status_command(&self, device_function: DeviceFunction) -> (Status, Command)166 pub fn get_status_command(&self, device_function: DeviceFunction) -> (Status, Command) { 167 let status_command = self 168 .configuration_access 169 .read_word(device_function, STATUS_COMMAND_OFFSET); 170 let status = Status::from_bits_truncate((status_command >> 16) as u16); 171 let command = Command::from_bits_truncate(status_command as u16); 172 (status, command) 173 } 174 175 /// Sets the command register of the given device function. set_command(&mut self, device_function: DeviceFunction, command: Command)176 pub fn set_command(&mut self, device_function: DeviceFunction, command: Command) { 177 self.configuration_access.write_word( 178 device_function, 179 STATUS_COMMAND_OFFSET, 180 command.bits().into(), 181 ); 182 } 183 184 /// Gets an iterator over the capabilities of the given device function. capabilities(&self, device_function: DeviceFunction) -> CapabilityIterator<C>185 pub fn capabilities(&self, device_function: DeviceFunction) -> CapabilityIterator<C> { 186 CapabilityIterator { 187 configuration_access: &self.configuration_access, 188 device_function, 189 next_capability_offset: self.capabilities_offset(device_function), 190 } 191 } 192 193 /// Returns information about all the given device function's BARs. bars( &mut self, device_function: DeviceFunction, ) -> Result<[Option<BarInfo>; 6], PciError>194 pub fn bars( 195 &mut self, 196 device_function: DeviceFunction, 197 ) -> Result<[Option<BarInfo>; 6], PciError> { 198 let mut bars = array::from_fn(|_| None); 199 let mut bar_index = 0; 200 while bar_index < 6 { 201 let info = self.bar_info(device_function, bar_index)?; 202 let takes_two_entries = info.takes_two_entries(); 203 bars[usize::from(bar_index)] = Some(info); 204 bar_index += if takes_two_entries { 2 } else { 1 }; 205 } 206 Ok(bars) 207 } 208 209 /// Gets information about the given BAR of the given device function. bar_info( &mut self, device_function: DeviceFunction, bar_index: u8, ) -> Result<BarInfo, PciError>210 pub fn bar_info( 211 &mut self, 212 device_function: DeviceFunction, 213 bar_index: u8, 214 ) -> Result<BarInfo, PciError> { 215 let bar_orig = self 216 .configuration_access 217 .read_word(device_function, BAR0_OFFSET + 4 * bar_index); 218 219 // Get the size of the BAR. 220 self.configuration_access.write_word( 221 device_function, 222 BAR0_OFFSET + 4 * bar_index, 223 0xffffffff, 224 ); 225 let size_mask = self 226 .configuration_access 227 .read_word(device_function, BAR0_OFFSET + 4 * bar_index); 228 // A wrapping add is necessary to correctly handle the case of unused BARs, which read back 229 // as 0, and should be treated as size 0. 230 let size = (!(size_mask & 0xfffffff0)).wrapping_add(1); 231 232 // Restore the original value. 233 self.configuration_access.write_word( 234 device_function, 235 BAR0_OFFSET + 4 * bar_index, 236 bar_orig, 237 ); 238 239 if bar_orig & 0x00000001 == 0x00000001 { 240 // I/O space 241 let address = bar_orig & 0xfffffffc; 242 Ok(BarInfo::IO { address, size }) 243 } else { 244 // Memory space 245 let mut address = u64::from(bar_orig & 0xfffffff0); 246 let prefetchable = bar_orig & 0x00000008 != 0; 247 let address_type = MemoryBarType::try_from(((bar_orig & 0x00000006) >> 1) as u8)?; 248 if address_type == MemoryBarType::Width64 { 249 if bar_index >= 5 { 250 return Err(PciError::InvalidBarType); 251 } 252 let address_top = self 253 .configuration_access 254 .read_word(device_function, BAR0_OFFSET + 4 * (bar_index + 1)); 255 address |= u64::from(address_top) << 32; 256 } 257 Ok(BarInfo::Memory { 258 address_type, 259 prefetchable, 260 address, 261 size, 262 }) 263 } 264 } 265 266 /// Sets the address of the given 32-bit memory or I/O BAR of the given device function. set_bar_32(&mut self, device_function: DeviceFunction, bar_index: u8, address: u32)267 pub fn set_bar_32(&mut self, device_function: DeviceFunction, bar_index: u8, address: u32) { 268 self.configuration_access 269 .write_word(device_function, BAR0_OFFSET + 4 * bar_index, address); 270 } 271 272 /// Sets the address of the given 64-bit memory BAR of the given device function. set_bar_64(&mut self, device_function: DeviceFunction, bar_index: u8, address: u64)273 pub fn set_bar_64(&mut self, device_function: DeviceFunction, bar_index: u8, address: u64) { 274 self.configuration_access.write_word( 275 device_function, 276 BAR0_OFFSET + 4 * bar_index, 277 address as u32, 278 ); 279 self.configuration_access.write_word( 280 device_function, 281 BAR0_OFFSET + 4 * (bar_index + 1), 282 (address >> 32) as u32, 283 ); 284 } 285 286 /// Gets the capabilities 'pointer' for the device function, if any. capabilities_offset(&self, device_function: DeviceFunction) -> Option<u8>287 fn capabilities_offset(&self, device_function: DeviceFunction) -> Option<u8> { 288 let (status, _) = self.get_status_command(device_function); 289 if status.contains(Status::CAPABILITIES_LIST) { 290 Some((self.configuration_access.read_word(device_function, 0x34) & 0xFC) as u8) 291 } else { 292 None 293 } 294 } 295 } 296 297 /// A method to access PCI configuration space for a particular PCI bus. 298 pub trait ConfigurationAccess { 299 /// Reads 4 bytes from the configuration space. read_word(&self, device_function: DeviceFunction, register_offset: u8) -> u32300 fn read_word(&self, device_function: DeviceFunction, register_offset: u8) -> u32; 301 302 /// Writes 4 bytes to the configuration space. write_word(&mut self, device_function: DeviceFunction, register_offset: u8, data: u32)303 fn write_word(&mut self, device_function: DeviceFunction, register_offset: u8, data: u32); 304 305 /// Makes a clone of the `ConfigurationAccess`, accessing the same PCI bus. 306 /// 307 /// # Safety 308 /// 309 /// This function allows concurrent mutable access to the PCI CAM. To avoid this causing 310 /// problems, the returned `ConfigurationAccess` instance must only be used to read read-only 311 /// fields. unsafe_clone(&self) -> Self312 unsafe fn unsafe_clone(&self) -> Self; 313 } 314 315 /// `ConfigurationAccess` implementation for memory-mapped access to a PCI root complex, via either 316 /// a 16 MiB region for the PCI Configuration Access Mechanism or a 256 MiB region for the PCIe 317 /// Enhanced Configuration Access Mechanism. 318 pub struct MmioCam { 319 mmio_base: *mut u32, 320 cam: Cam, 321 } 322 323 impl MmioCam { 324 /// Wraps the PCI root complex with the given MMIO base address. 325 /// 326 /// Panics if the base address is not aligned to a 4-byte boundary. 327 /// 328 /// # Safety 329 /// 330 /// `mmio_base` must be a valid pointer to an appropriately-mapped MMIO region of at least 331 /// 16 MiB (if `cam == Cam::MmioCam`) or 256 MiB (if `cam == Cam::Ecam`). The pointer must be 332 /// valid for the entire lifetime of the program (i.e. `'static`), which implies that no Rust 333 /// references may be used to access any of the memory region at any point. new(mmio_base: *mut u8, cam: Cam) -> Self334 pub unsafe fn new(mmio_base: *mut u8, cam: Cam) -> Self { 335 assert!(mmio_base as usize & 0x3 == 0); 336 Self { 337 mmio_base: mmio_base as *mut u32, 338 cam, 339 } 340 } 341 } 342 343 impl ConfigurationAccess for MmioCam { read_word(&self, device_function: DeviceFunction, register_offset: u8) -> u32344 fn read_word(&self, device_function: DeviceFunction, register_offset: u8) -> u32 { 345 let address = self.cam.cam_offset(device_function, register_offset); 346 // Safe because both the `mmio_base` and the address offset are properly aligned, and the 347 // resulting pointer is within the MMIO range of the CAM. 348 unsafe { 349 // Right shift to convert from byte offset to word offset. 350 (self.mmio_base.add((address >> 2) as usize)).read_volatile() 351 } 352 } 353 write_word(&mut self, device_function: DeviceFunction, register_offset: u8, data: u32)354 fn write_word(&mut self, device_function: DeviceFunction, register_offset: u8, data: u32) { 355 let address = self.cam.cam_offset(device_function, register_offset); 356 // Safe because both the `mmio_base` and the address offset are properly aligned, and the 357 // resulting pointer is within the MMIO range of the CAM. 358 unsafe { 359 // Right shift to convert from byte offset to word offset. 360 (self.mmio_base.add((address >> 2) as usize)).write_volatile(data) 361 } 362 } 363 unsafe_clone(&self) -> Self364 unsafe fn unsafe_clone(&self) -> Self { 365 Self { 366 mmio_base: self.mmio_base, 367 cam: self.cam, 368 } 369 } 370 } 371 372 // SAFETY: `mmio_base` is only used for MMIO, which can happen from any thread or CPU core. 373 unsafe impl Send for MmioCam {} 374 375 // SAFETY: `&MmioCam` only allows MMIO reads, which are fine to happen concurrently on different CPU 376 // cores. 377 unsafe impl Sync for MmioCam {} 378 379 /// Information about a PCI Base Address Register. 380 #[derive(Clone, Debug, Eq, PartialEq)] 381 pub enum BarInfo { 382 /// The BAR is for a memory region. 383 Memory { 384 /// The size of the BAR address and where it can be located. 385 address_type: MemoryBarType, 386 /// If true, then reading from the region doesn't have side effects. The CPU may cache reads 387 /// and merge repeated stores. 388 prefetchable: bool, 389 /// The memory address, always 16-byte aligned. 390 address: u64, 391 /// The size of the BAR in bytes. 392 size: u32, 393 }, 394 /// The BAR is for an I/O region. 395 IO { 396 /// The I/O address, always 4-byte aligned. 397 address: u32, 398 /// The size of the BAR in bytes. 399 size: u32, 400 }, 401 } 402 403 impl BarInfo { 404 /// Returns whether this BAR is a 64-bit memory region, and so takes two entries in the table in 405 /// configuration space. takes_two_entries(&self) -> bool406 pub fn takes_two_entries(&self) -> bool { 407 matches!( 408 self, 409 BarInfo::Memory { 410 address_type: MemoryBarType::Width64, 411 .. 412 } 413 ) 414 } 415 416 /// Returns the address and size of this BAR if it is a memory bar, or `None` if it is an IO 417 /// BAR. memory_address_size(&self) -> Option<(u64, u32)>418 pub fn memory_address_size(&self) -> Option<(u64, u32)> { 419 if let Self::Memory { address, size, .. } = self { 420 Some((*address, *size)) 421 } else { 422 None 423 } 424 } 425 } 426 427 impl Display for BarInfo { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result428 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 429 match self { 430 Self::Memory { 431 address_type, 432 prefetchable, 433 address, 434 size, 435 } => write!( 436 f, 437 "Memory space at {:#010x}, size {}, type {:?}, prefetchable {}", 438 address, size, address_type, prefetchable 439 ), 440 Self::IO { address, size } => { 441 write!(f, "I/O space at {:#010x}, size {}", address, size) 442 } 443 } 444 } 445 } 446 447 /// The location allowed for a memory BAR. 448 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 449 pub enum MemoryBarType { 450 /// The BAR has a 32-bit address and can be mapped anywhere in 32-bit address space. 451 Width32, 452 /// The BAR must be mapped below 1MiB. 453 Below1MiB, 454 /// The BAR has a 64-bit address and can be mapped anywhere in 64-bit address space. 455 Width64, 456 } 457 458 impl From<MemoryBarType> for u8 { from(bar_type: MemoryBarType) -> Self459 fn from(bar_type: MemoryBarType) -> Self { 460 match bar_type { 461 MemoryBarType::Width32 => 0, 462 MemoryBarType::Below1MiB => 1, 463 MemoryBarType::Width64 => 2, 464 } 465 } 466 } 467 468 impl TryFrom<u8> for MemoryBarType { 469 type Error = PciError; 470 try_from(value: u8) -> Result<Self, Self::Error>471 fn try_from(value: u8) -> Result<Self, Self::Error> { 472 match value { 473 0 => Ok(Self::Width32), 474 1 => Ok(Self::Below1MiB), 475 2 => Ok(Self::Width64), 476 _ => Err(PciError::InvalidBarType), 477 } 478 } 479 } 480 481 /// Iterator over capabilities for a device. 482 #[derive(Debug)] 483 pub struct CapabilityIterator<'a, C: ConfigurationAccess> { 484 configuration_access: &'a C, 485 device_function: DeviceFunction, 486 next_capability_offset: Option<u8>, 487 } 488 489 impl<C: ConfigurationAccess> Iterator for CapabilityIterator<'_, C> { 490 type Item = CapabilityInfo; 491 next(&mut self) -> Option<Self::Item>492 fn next(&mut self) -> Option<Self::Item> { 493 let offset = self.next_capability_offset?; 494 495 // Read the first 4 bytes of the capability. 496 let capability_header = self 497 .configuration_access 498 .read_word(self.device_function, offset); 499 let id = capability_header as u8; 500 let next_offset = (capability_header >> 8) as u8; 501 let private_header = (capability_header >> 16) as u16; 502 503 self.next_capability_offset = if next_offset == 0 { 504 None 505 } else if next_offset < 64 || next_offset & 0x3 != 0 { 506 warn!("Invalid next capability offset {:#04x}", next_offset); 507 None 508 } else { 509 Some(next_offset) 510 }; 511 512 Some(CapabilityInfo { 513 offset, 514 id, 515 private_header, 516 }) 517 } 518 } 519 520 /// Information about a PCI device capability. 521 #[derive(Debug, Copy, Clone, Eq, PartialEq)] 522 pub struct CapabilityInfo { 523 /// The offset of the capability in the PCI configuration space of the device function. 524 pub offset: u8, 525 /// The ID of the capability. 526 pub id: u8, 527 /// The third and fourth bytes of the capability, to save reading them again. 528 pub private_header: u16, 529 } 530 531 /// An iterator which enumerates PCI devices and functions on a given bus. 532 #[derive(Debug)] 533 pub struct BusDeviceIterator<C: ConfigurationAccess> { 534 /// This must only be used to read read-only fields, and must not be exposed outside this 535 /// module, because it uses the same CAM as the main `PciRoot` instance. 536 configuration_access: C, 537 next: DeviceFunction, 538 } 539 540 impl<C: ConfigurationAccess> Iterator for BusDeviceIterator<C> { 541 type Item = (DeviceFunction, DeviceFunctionInfo); 542 next(&mut self) -> Option<Self::Item>543 fn next(&mut self) -> Option<Self::Item> { 544 while self.next.device < MAX_DEVICES { 545 // Read the header for the current device and function. 546 let current = self.next; 547 let device_vendor = self.configuration_access.read_word(current, 0); 548 549 // Advance to the next device or function. 550 self.next.function += 1; 551 if self.next.function >= MAX_FUNCTIONS { 552 self.next.function = 0; 553 self.next.device += 1; 554 } 555 556 if device_vendor != INVALID_READ { 557 let class_revision = self.configuration_access.read_word(current, 8); 558 let device_id = (device_vendor >> 16) as u16; 559 let vendor_id = device_vendor as u16; 560 let class = (class_revision >> 24) as u8; 561 let subclass = (class_revision >> 16) as u8; 562 let prog_if = (class_revision >> 8) as u8; 563 let revision = class_revision as u8; 564 let bist_type_latency_cache = self.configuration_access.read_word(current, 12); 565 let header_type = HeaderType::from((bist_type_latency_cache >> 16) as u8 & 0x7f); 566 return Some(( 567 current, 568 DeviceFunctionInfo { 569 vendor_id, 570 device_id, 571 class, 572 subclass, 573 prog_if, 574 revision, 575 header_type, 576 }, 577 )); 578 } 579 } 580 None 581 } 582 } 583 584 /// An identifier for a PCI bus, device and function. 585 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 586 pub struct DeviceFunction { 587 /// The PCI bus number, between 0 and 255. 588 pub bus: u8, 589 /// The device number on the bus, between 0 and 31. 590 pub device: u8, 591 /// The function number of the device, between 0 and 7. 592 pub function: u8, 593 } 594 595 impl DeviceFunction { 596 /// Returns whether the device and function numbers are valid, i.e. the device is between 0 and 597 /// 31, and the function is between 0 and 7. valid(&self) -> bool598 pub fn valid(&self) -> bool { 599 self.device < 32 && self.function < 8 600 } 601 } 602 603 impl Display for DeviceFunction { fmt(&self, f: &mut Formatter) -> fmt::Result604 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 605 write!(f, "{:02x}:{:02x}.{}", self.bus, self.device, self.function) 606 } 607 } 608 609 /// Information about a PCI device function. 610 #[derive(Clone, Debug, Eq, PartialEq)] 611 pub struct DeviceFunctionInfo { 612 /// The PCI vendor ID. 613 pub vendor_id: u16, 614 /// The PCI device ID. 615 pub device_id: u16, 616 /// The PCI class. 617 pub class: u8, 618 /// The PCI subclass. 619 pub subclass: u8, 620 /// The PCI programming interface byte. 621 pub prog_if: u8, 622 /// The PCI revision ID. 623 pub revision: u8, 624 /// The type of PCI device. 625 pub header_type: HeaderType, 626 } 627 628 impl Display for DeviceFunctionInfo { fmt(&self, f: &mut Formatter) -> fmt::Result629 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 630 write!( 631 f, 632 "{:04x}:{:04x} (class {:02x}.{:02x}, rev {:02x}) {:?}", 633 self.vendor_id, 634 self.device_id, 635 self.class, 636 self.subclass, 637 self.revision, 638 self.header_type, 639 ) 640 } 641 } 642 643 /// The type of a PCI device function header. 644 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 645 pub enum HeaderType { 646 /// A normal PCI device. 647 Standard, 648 /// A PCI to PCI bridge. 649 PciPciBridge, 650 /// A PCI to CardBus bridge. 651 PciCardbusBridge, 652 /// Unrecognised header type. 653 Unrecognised(u8), 654 } 655 656 impl From<u8> for HeaderType { from(value: u8) -> Self657 fn from(value: u8) -> Self { 658 match value { 659 0x00 => Self::Standard, 660 0x01 => Self::PciPciBridge, 661 0x02 => Self::PciCardbusBridge, 662 _ => Self::Unrecognised(value), 663 } 664 } 665 } 666