• 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             let ptr = self.mmio_base.add((address >> 2) as usize);
361             #[cfg(not(target_arch = "aarch64"))]
362             {
363                 ptr.write_volatile(data)
364             }
365             #[cfg(target_arch = "aarch64")]
366             {
367                 core::arch::asm!(
368                     "str {value:w}, [{ptr}]",
369                     value = in(reg) data,
370                     ptr = in(reg) ptr,
371                 )
372             }
373         }
374     }
375 
unsafe_clone(&self) -> Self376     unsafe fn unsafe_clone(&self) -> Self {
377         Self {
378             mmio_base: self.mmio_base,
379             cam: self.cam,
380         }
381     }
382 }
383 
384 // SAFETY: `mmio_base` is only used for MMIO, which can happen from any thread or CPU core.
385 unsafe impl Send for MmioCam {}
386 
387 // SAFETY: `&MmioCam` only allows MMIO reads, which are fine to happen concurrently on different CPU
388 // cores.
389 unsafe impl Sync for MmioCam {}
390 
391 /// Information about a PCI Base Address Register.
392 #[derive(Clone, Debug, Eq, PartialEq)]
393 pub enum BarInfo {
394     /// The BAR is for a memory region.
395     Memory {
396         /// The size of the BAR address and where it can be located.
397         address_type: MemoryBarType,
398         /// If true, then reading from the region doesn't have side effects. The CPU may cache reads
399         /// and merge repeated stores.
400         prefetchable: bool,
401         /// The memory address, always 16-byte aligned.
402         address: u64,
403         /// The size of the BAR in bytes.
404         size: u32,
405     },
406     /// The BAR is for an I/O region.
407     IO {
408         /// The I/O address, always 4-byte aligned.
409         address: u32,
410         /// The size of the BAR in bytes.
411         size: u32,
412     },
413 }
414 
415 impl BarInfo {
416     /// Returns whether this BAR is a 64-bit memory region, and so takes two entries in the table in
417     /// configuration space.
takes_two_entries(&self) -> bool418     pub fn takes_two_entries(&self) -> bool {
419         matches!(
420             self,
421             BarInfo::Memory {
422                 address_type: MemoryBarType::Width64,
423                 ..
424             }
425         )
426     }
427 
428     /// Returns the address and size of this BAR if it is a memory bar, or `None` if it is an IO
429     /// BAR.
memory_address_size(&self) -> Option<(u64, u32)>430     pub fn memory_address_size(&self) -> Option<(u64, u32)> {
431         if let Self::Memory { address, size, .. } = self {
432             Some((*address, *size))
433         } else {
434             None
435         }
436     }
437 }
438 
439 impl Display for BarInfo {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result440     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
441         match self {
442             Self::Memory {
443                 address_type,
444                 prefetchable,
445                 address,
446                 size,
447             } => write!(
448                 f,
449                 "Memory space at {:#010x}, size {}, type {:?}, prefetchable {}",
450                 address, size, address_type, prefetchable
451             ),
452             Self::IO { address, size } => {
453                 write!(f, "I/O space at {:#010x}, size {}", address, size)
454             }
455         }
456     }
457 }
458 
459 /// The location allowed for a memory BAR.
460 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
461 pub enum MemoryBarType {
462     /// The BAR has a 32-bit address and can be mapped anywhere in 32-bit address space.
463     Width32,
464     /// The BAR must be mapped below 1MiB.
465     Below1MiB,
466     /// The BAR has a 64-bit address and can be mapped anywhere in 64-bit address space.
467     Width64,
468 }
469 
470 impl From<MemoryBarType> for u8 {
from(bar_type: MemoryBarType) -> Self471     fn from(bar_type: MemoryBarType) -> Self {
472         match bar_type {
473             MemoryBarType::Width32 => 0,
474             MemoryBarType::Below1MiB => 1,
475             MemoryBarType::Width64 => 2,
476         }
477     }
478 }
479 
480 impl TryFrom<u8> for MemoryBarType {
481     type Error = PciError;
482 
try_from(value: u8) -> Result<Self, Self::Error>483     fn try_from(value: u8) -> Result<Self, Self::Error> {
484         match value {
485             0 => Ok(Self::Width32),
486             1 => Ok(Self::Below1MiB),
487             2 => Ok(Self::Width64),
488             _ => Err(PciError::InvalidBarType),
489         }
490     }
491 }
492 
493 /// Iterator over capabilities for a device.
494 #[derive(Debug)]
495 pub struct CapabilityIterator<'a, C: ConfigurationAccess> {
496     configuration_access: &'a C,
497     device_function: DeviceFunction,
498     next_capability_offset: Option<u8>,
499 }
500 
501 impl<C: ConfigurationAccess> Iterator for CapabilityIterator<'_, C> {
502     type Item = CapabilityInfo;
503 
next(&mut self) -> Option<Self::Item>504     fn next(&mut self) -> Option<Self::Item> {
505         let offset = self.next_capability_offset?;
506 
507         // Read the first 4 bytes of the capability.
508         let capability_header = self
509             .configuration_access
510             .read_word(self.device_function, offset);
511         let id = capability_header as u8;
512         let next_offset = (capability_header >> 8) as u8;
513         let private_header = (capability_header >> 16) as u16;
514 
515         self.next_capability_offset = if next_offset == 0 {
516             None
517         } else if next_offset < 64 || next_offset & 0x3 != 0 {
518             warn!("Invalid next capability offset {:#04x}", next_offset);
519             None
520         } else {
521             Some(next_offset)
522         };
523 
524         Some(CapabilityInfo {
525             offset,
526             id,
527             private_header,
528         })
529     }
530 }
531 
532 /// Information about a PCI device capability.
533 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
534 pub struct CapabilityInfo {
535     /// The offset of the capability in the PCI configuration space of the device function.
536     pub offset: u8,
537     /// The ID of the capability.
538     pub id: u8,
539     /// The third and fourth bytes of the capability, to save reading them again.
540     pub private_header: u16,
541 }
542 
543 /// An iterator which enumerates PCI devices and functions on a given bus.
544 #[derive(Debug)]
545 pub struct BusDeviceIterator<C: ConfigurationAccess> {
546     /// This must only be used to read read-only fields, and must not be exposed outside this
547     /// module, because it uses the same CAM as the main `PciRoot` instance.
548     configuration_access: C,
549     next: DeviceFunction,
550 }
551 
552 impl<C: ConfigurationAccess> Iterator for BusDeviceIterator<C> {
553     type Item = (DeviceFunction, DeviceFunctionInfo);
554 
next(&mut self) -> Option<Self::Item>555     fn next(&mut self) -> Option<Self::Item> {
556         while self.next.device < MAX_DEVICES {
557             // Read the header for the current device and function.
558             let current = self.next;
559             let device_vendor = self.configuration_access.read_word(current, 0);
560 
561             // Advance to the next device or function.
562             self.next.function += 1;
563             if self.next.function >= MAX_FUNCTIONS {
564                 self.next.function = 0;
565                 self.next.device += 1;
566             }
567 
568             if device_vendor != INVALID_READ {
569                 let class_revision = self.configuration_access.read_word(current, 8);
570                 let device_id = (device_vendor >> 16) as u16;
571                 let vendor_id = device_vendor as u16;
572                 let class = (class_revision >> 24) as u8;
573                 let subclass = (class_revision >> 16) as u8;
574                 let prog_if = (class_revision >> 8) as u8;
575                 let revision = class_revision as u8;
576                 let bist_type_latency_cache = self.configuration_access.read_word(current, 12);
577                 let header_type = HeaderType::from((bist_type_latency_cache >> 16) as u8 & 0x7f);
578                 return Some((
579                     current,
580                     DeviceFunctionInfo {
581                         vendor_id,
582                         device_id,
583                         class,
584                         subclass,
585                         prog_if,
586                         revision,
587                         header_type,
588                     },
589                 ));
590             }
591         }
592         None
593     }
594 }
595 
596 /// An identifier for a PCI bus, device and function.
597 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
598 pub struct DeviceFunction {
599     /// The PCI bus number, between 0 and 255.
600     pub bus: u8,
601     /// The device number on the bus, between 0 and 31.
602     pub device: u8,
603     /// The function number of the device, between 0 and 7.
604     pub function: u8,
605 }
606 
607 impl DeviceFunction {
608     /// Returns whether the device and function numbers are valid, i.e. the device is between 0 and
609     /// 31, and the function is between 0 and 7.
valid(&self) -> bool610     pub fn valid(&self) -> bool {
611         self.device < 32 && self.function < 8
612     }
613 }
614 
615 impl Display for DeviceFunction {
fmt(&self, f: &mut Formatter) -> fmt::Result616     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
617         write!(f, "{:02x}:{:02x}.{}", self.bus, self.device, self.function)
618     }
619 }
620 
621 /// Information about a PCI device function.
622 #[derive(Clone, Debug, Eq, PartialEq)]
623 pub struct DeviceFunctionInfo {
624     /// The PCI vendor ID.
625     pub vendor_id: u16,
626     /// The PCI device ID.
627     pub device_id: u16,
628     /// The PCI class.
629     pub class: u8,
630     /// The PCI subclass.
631     pub subclass: u8,
632     /// The PCI programming interface byte.
633     pub prog_if: u8,
634     /// The PCI revision ID.
635     pub revision: u8,
636     /// The type of PCI device.
637     pub header_type: HeaderType,
638 }
639 
640 impl Display for DeviceFunctionInfo {
fmt(&self, f: &mut Formatter) -> fmt::Result641     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
642         write!(
643             f,
644             "{:04x}:{:04x} (class {:02x}.{:02x}, rev {:02x}) {:?}",
645             self.vendor_id,
646             self.device_id,
647             self.class,
648             self.subclass,
649             self.revision,
650             self.header_type,
651         )
652     }
653 }
654 
655 /// The type of a PCI device function header.
656 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
657 pub enum HeaderType {
658     /// A normal PCI device.
659     Standard,
660     /// A PCI to PCI bridge.
661     PciPciBridge,
662     /// A PCI to CardBus bridge.
663     PciCardbusBridge,
664     /// Unrecognised header type.
665     Unrecognised(u8),
666 }
667 
668 impl From<u8> for HeaderType {
from(value: u8) -> Self669     fn from(value: u8) -> Self {
670         match value {
671             0x00 => Self::Standard,
672             0x01 => Self::PciPciBridge,
673             0x02 => Self::PciCardbusBridge,
674             _ => Self::Unrecognised(value),
675         }
676     }
677 }
678