• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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