Skip to main content

virtio_drivers/transport/pci/
bus.rs

1//! Module for dealing with a PCI bus in general, without anything specific to VirtIO.
2
3use bitflags::bitflags;
4use core::{
5    array,
6    convert::TryFrom,
7    fmt::{self, Display, Formatter},
8    ops::Deref,
9    ptr::NonNull,
10};
11use log::warn;
12use safe_mmio::{UniqueMmioPointer, fields::ReadPureWrite};
13use thiserror::Error;
14
15const INVALID_READ: u32 = 0xffffffff;
16
17/// The maximum number of devices on a bus.
18const MAX_DEVICES: u8 = 32;
19/// The maximum number of functions on a device.
20const MAX_FUNCTIONS: u8 = 8;
21
22/// The offset in bytes to the status and command fields within PCI configuration space.
23const STATUS_COMMAND_OFFSET: u8 = 0x04;
24/// The offset in bytes to BAR0 within PCI configuration space.
25const BAR0_OFFSET: u8 = 0x10;
26
27/// ID for vendor-specific PCI capabilities.
28pub const PCI_CAP_ID_VNDR: u8 = 0x09;
29
30bitflags! {
31    /// The status register in PCI configuration space.
32    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
33    pub struct Status: u16 {
34        // Bits 0-2 are reserved.
35        /// The state of the device's INTx# signal.
36        const INTERRUPT_STATUS = 1 << 3;
37        /// The device has a linked list of capabilities.
38        const CAPABILITIES_LIST = 1 << 4;
39        /// The device is capabile of running at 66 MHz rather than 33 MHz.
40        const MHZ_66_CAPABLE = 1 << 5;
41        // Bit 6 is reserved.
42        /// The device can accept fast back-to-back transactions not from the same agent.
43        const FAST_BACK_TO_BACK_CAPABLE = 1 << 7;
44        /// The bus agent observed a parity error (if parity error handling is enabled).
45        const MASTER_DATA_PARITY_ERROR = 1 << 8;
46        // Bits 9-10 are DEVSEL timing.
47        /// A target device terminated a transaction with target-abort.
48        const SIGNALED_TARGET_ABORT = 1 << 11;
49        /// A master device transaction was terminated with target-abort.
50        const RECEIVED_TARGET_ABORT = 1 << 12;
51        /// A master device transaction was terminated with master-abort.
52        const RECEIVED_MASTER_ABORT = 1 << 13;
53        /// A device asserts SERR#.
54        const SIGNALED_SYSTEM_ERROR = 1 << 14;
55        /// The device detects a parity error, even if parity error handling is disabled.
56        const DETECTED_PARITY_ERROR = 1 << 15;
57    }
58}
59
60bitflags! {
61    /// The command register in PCI configuration space.
62    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
63    pub struct Command: u16 {
64        /// The device can respond to I/O Space accesses.
65        const IO_SPACE = 1 << 0;
66        /// The device can respond to Memory Space accesses.
67        const MEMORY_SPACE = 1 << 1;
68        /// The device can behave as a bus master.
69        const BUS_MASTER = 1 << 2;
70        /// The device can monitor Special Cycle operations.
71        const SPECIAL_CYCLES = 1 << 3;
72        /// The device can generate the Memory Write and Invalidate command.
73        const MEMORY_WRITE_AND_INVALIDATE_ENABLE = 1 << 4;
74        /// The device will snoop palette register data.
75        const VGA_PALETTE_SNOOP = 1 << 5;
76        /// The device should take its normal action when a parity error is detected.
77        const PARITY_ERROR_RESPONSE = 1 << 6;
78        // Bit 7 is reserved.
79        /// The SERR# driver is enabled.
80        const SERR_ENABLE = 1 << 8;
81        /// The device is allowed to generate fast back-to-back transactions.
82        const FAST_BACK_TO_BACK_ENABLE = 1 << 9;
83        /// Assertion of the device's INTx# signal is disabled.
84        const INTERRUPT_DISABLE = 1 << 10;
85    }
86}
87
88/// Errors accessing a PCI device.
89#[derive(Copy, Clone, Debug, Eq, Error, PartialEq)]
90pub enum PciError {
91    /// The device reported an invalid BAR type.
92    #[error("Invalid PCI BAR type")]
93    InvalidBarType,
94}
95
96/// The root complex of a PCI bus.
97#[derive(Debug)]
98pub struct PciRoot<C: ConfigurationAccess> {
99    pub(crate) configuration_access: C,
100}
101
102/// A PCI Configuration Access Mechanism.
103#[derive(Copy, Clone, Debug, Eq, PartialEq)]
104pub enum Cam {
105    /// The PCI memory-mapped Configuration Access Mechanism.
106    ///
107    /// This provides access to 256 bytes of configuration space per device function.
108    MmioCam,
109    /// The PCIe memory-mapped Enhanced Configuration Access Mechanism.
110    ///
111    /// This provides access to 4 KiB of configuration space per device function.
112    Ecam,
113}
114
115impl Cam {
116    /// Returns the total size in bytes of the memory-mapped region.
117    pub const fn size(self) -> u32 {
118        match self {
119            Self::MmioCam => 0x1000000,
120            Self::Ecam => 0x10000000,
121        }
122    }
123
124    /// Returns the offset in bytes within the CAM region for the given device, function and
125    /// register.
126    pub fn cam_offset(self, device_function: DeviceFunction, register_offset: u8) -> u32 {
127        assert!(device_function.valid());
128
129        let bdf = ((device_function.bus as u32) << 8)
130            | ((device_function.device as u32) << 3)
131            | (device_function.function as u32);
132        let address =
133            (bdf << match self {
134                Cam::MmioCam => 8,
135                Cam::Ecam => 12,
136            }) | (register_offset as u32);
137        // Ensure that address is within range.
138        assert!(address < self.size());
139        // Ensure that address is word-aligned.
140        assert!(address & 0x3 == 0);
141        address
142    }
143}
144
145impl<C: ConfigurationAccess> PciRoot<C> {
146    /// Creates a new `PciRoot` to access a PCI root complex through the given configuration access
147    /// implementation.
148    pub fn new(configuration_access: C) -> Self {
149        Self {
150            configuration_access,
151        }
152    }
153
154    /// Enumerates PCI devices on the given bus.
155    pub fn enumerate_bus(&self, bus: u8) -> BusDeviceIterator<C> {
156        // SAFETY: The `BusDeviceIterator` only reads read-only fields.
157        let configuration_access = unsafe { self.configuration_access.unsafe_clone() };
158        BusDeviceIterator {
159            configuration_access,
160            next: DeviceFunction {
161                bus,
162                device: 0,
163                function: 0,
164            },
165        }
166    }
167
168    /// Reads the status and command registers of the given device function.
169    pub fn get_status_command(&self, device_function: DeviceFunction) -> (Status, Command) {
170        let status_command = self
171            .configuration_access
172            .read_word(device_function, STATUS_COMMAND_OFFSET);
173        let status = Status::from_bits_truncate((status_command >> 16) as u16);
174        let command = Command::from_bits_truncate(status_command as u16);
175        (status, command)
176    }
177
178    /// Sets the command register of the given device function.
179    pub fn set_command(&mut self, device_function: DeviceFunction, command: Command) {
180        self.configuration_access.write_word(
181            device_function,
182            STATUS_COMMAND_OFFSET,
183            command.bits().into(),
184        );
185    }
186
187    /// Gets an iterator over the capabilities of the given device function.
188    pub fn capabilities(&self, device_function: DeviceFunction) -> CapabilityIterator<'_, C> {
189        CapabilityIterator {
190            configuration_access: &self.configuration_access,
191            device_function,
192            next_capability_offset: self.capabilities_offset(device_function),
193        }
194    }
195
196    /// Returns information about all the given device function's BARs.
197    pub fn bars(
198        &mut self,
199        device_function: DeviceFunction,
200    ) -> Result<[Option<BarInfo>; 6], PciError> {
201        let mut bars = array::from_fn(|_| None);
202        let mut bar_index = 0;
203        while bar_index < 6 {
204            let info = self.bar_info(device_function, bar_index)?;
205            let bar_entries = if info.as_ref().is_some_and(BarInfo::takes_two_entries) {
206                2
207            } else {
208                1
209            };
210            bars[usize::from(bar_index)] = info;
211            bar_index += bar_entries;
212        }
213        Ok(bars)
214    }
215
216    /// Gets information about the given BAR of the given device function.
217    pub fn bar_info(
218        &mut self,
219        device_function: DeviceFunction,
220        bar_index: u8,
221    ) -> Result<Option<BarInfo>, PciError> {
222        // Disable address decoding while sizing the BAR.
223        let (_status, command_orig) = self.get_status_command(device_function);
224        let command_disable_decode = command_orig & !(Command::IO_SPACE | Command::MEMORY_SPACE);
225        if command_disable_decode != command_orig {
226            self.set_command(device_function, command_disable_decode);
227        }
228
229        let bar_orig = self
230            .configuration_access
231            .read_word(device_function, BAR0_OFFSET + 4 * bar_index);
232        let io_space = bar_orig & 0x00000001 == 0x00000001;
233
234        // Get the size of the BAR.
235        self.configuration_access.write_word(
236            device_function,
237            BAR0_OFFSET + 4 * bar_index,
238            0xffffffff,
239        );
240        let mut size_mask = u64::from(
241            self.configuration_access
242                .read_word(device_function, BAR0_OFFSET + 4 * bar_index),
243        );
244
245        // Read the upper 32 bits of 64-bit memory BARs.
246        let (address_top, size_top) = if bar_orig & 0b111 == 0b100 {
247            if bar_index >= 5 {
248                return Err(PciError::InvalidBarType);
249            }
250            let bar_top_orig = self
251                .configuration_access
252                .read_word(device_function, BAR0_OFFSET + 4 * (bar_index + 1));
253            self.configuration_access.write_word(
254                device_function,
255                BAR0_OFFSET + 4 * (bar_index + 1),
256                0xffffffff,
257            );
258            let size_top = self
259                .configuration_access
260                .read_word(device_function, BAR0_OFFSET + 4 * (bar_index + 1));
261            self.configuration_access.write_word(
262                device_function,
263                BAR0_OFFSET + 4 * (bar_index + 1),
264                bar_top_orig,
265            );
266            (bar_top_orig, size_top)
267        } else {
268            let size_top = if size_mask == 0 { 0 } else { 0xffffffff };
269            (0, size_top)
270        };
271        size_mask |= u64::from(size_top) << 32;
272
273        // For IO BARs bits 2 and 3 can be part of the address.
274        let flag_bits = if io_space { 0b11 } else { 0b1111 };
275        // A wrapping add is necessary to correctly handle the case of unused BARs, which read back
276        // as 0, and should be treated as size 0.
277        let size = (!(size_mask & !flag_bits)).wrapping_add(1);
278
279        // Restore the original value.
280        self.configuration_access.write_word(
281            device_function,
282            BAR0_OFFSET + 4 * bar_index,
283            bar_orig,
284        );
285
286        if command_disable_decode != command_orig {
287            self.set_command(device_function, command_orig);
288        }
289
290        if size_mask == 0 {
291            Ok(None)
292        } else if io_space {
293            // I/O space
294            let address = bar_orig & 0xfffffffc;
295            Ok(Some(BarInfo::IO {
296                address,
297                size: size as u32,
298            }))
299        } else {
300            // Memory space
301            let address = u64::from(bar_orig & 0xfffffff0) | (u64::from(address_top) << 32);
302            let prefetchable = bar_orig & 0x00000008 != 0;
303            let address_type = MemoryBarType::try_from(((bar_orig & 0x00000006) >> 1) as u8)?;
304            Ok(Some(BarInfo::Memory {
305                address_type,
306                prefetchable,
307                address,
308                size,
309            }))
310        }
311    }
312
313    /// Sets the address of the given 32-bit memory or I/O BAR of the given device function.
314    pub fn set_bar_32(&mut self, device_function: DeviceFunction, bar_index: u8, address: u32) {
315        self.configuration_access
316            .write_word(device_function, BAR0_OFFSET + 4 * bar_index, address);
317    }
318
319    /// Sets the address of the given 64-bit memory BAR of the given device function.
320    pub fn set_bar_64(&mut self, device_function: DeviceFunction, bar_index: u8, address: u64) {
321        self.configuration_access.write_word(
322            device_function,
323            BAR0_OFFSET + 4 * bar_index,
324            address as u32,
325        );
326        self.configuration_access.write_word(
327            device_function,
328            BAR0_OFFSET + 4 * (bar_index + 1),
329            (address >> 32) as u32,
330        );
331    }
332
333    /// Gets the capabilities 'pointer' for the device function, if any.
334    fn capabilities_offset(&self, device_function: DeviceFunction) -> Option<u8> {
335        let (status, _) = self.get_status_command(device_function);
336        if status.contains(Status::CAPABILITIES_LIST) {
337            Some((self.configuration_access.read_word(device_function, 0x34) & 0xFC) as u8)
338        } else {
339            None
340        }
341    }
342}
343
344/// A method to access PCI configuration space for a particular PCI bus.
345pub trait ConfigurationAccess {
346    /// Reads 4 bytes from the configuration space.
347    fn read_word(&self, device_function: DeviceFunction, register_offset: u8) -> u32;
348
349    /// Writes 4 bytes to the configuration space.
350    fn write_word(&mut self, device_function: DeviceFunction, register_offset: u8, data: u32);
351
352    /// Makes a clone of the `ConfigurationAccess`, accessing the same PCI bus.
353    ///
354    /// # Safety
355    ///
356    /// This function allows concurrent mutable access to the PCI CAM. To avoid this causing
357    /// problems, the returned `ConfigurationAccess` instance must only be used to read read-only
358    /// fields.
359    unsafe fn unsafe_clone(&self) -> Self;
360}
361
362/// `ConfigurationAccess` implementation for memory-mapped access to a PCI root complex, via either
363/// a 16 MiB region for the PCI Configuration Access Mechanism or a 256 MiB region for the PCIe
364/// Enhanced Configuration Access Mechanism.
365pub struct MmioCam<'a> {
366    mmio: UniqueMmioPointer<'a, [ReadPureWrite<u32>]>,
367    cam: Cam,
368}
369
370impl MmioCam<'_> {
371    /// Wraps the PCI root complex with the given MMIO base address.
372    ///
373    /// Panics if the base address is not aligned to a 4-byte boundary.
374    ///
375    /// # Safety
376    ///
377    /// `mmio_base` must be a valid pointer to an appropriately-mapped MMIO region of at least
378    /// 16 MiB (if `cam == Cam::MmioCam`) or 256 MiB (if `cam == Cam::Ecam`). The pointer must be
379    /// valid for the lifetime `'a`, which implies that no Rust references may be used to access any
380    /// of the memory region at least during that lifetime.
381    pub unsafe fn new(mmio_base: *mut u8, cam: Cam) -> Self {
382        assert!(mmio_base as usize & 0x3 == 0);
383        Self {
384            // SAFETY: The caller promised that `mmio_base` is a valid pointer to an MMIO region of
385            // sufficient size and lifetime.
386            mmio: unsafe {
387                UniqueMmioPointer::new(NonNull::slice_from_raw_parts(
388                    NonNull::new(mmio_base as *mut ReadPureWrite<u32>).unwrap(),
389                    cam.size() as usize / size_of::<u32>(),
390                ))
391            },
392            cam,
393        }
394    }
395}
396
397impl ConfigurationAccess for MmioCam<'_> {
398    fn read_word(&self, device_function: DeviceFunction, register_offset: u8) -> u32 {
399        let address = self.cam.cam_offset(device_function, register_offset);
400        // Right shift to convert from byte offset to word offset.
401        self.mmio
402            .deref()
403            .get((address >> 2) as usize)
404            .unwrap()
405            .read()
406    }
407
408    fn write_word(&mut self, device_function: DeviceFunction, register_offset: u8, data: u32) {
409        let address = self.cam.cam_offset(device_function, register_offset);
410        self.mmio.get((address >> 2) as usize).unwrap().write(data);
411    }
412
413    unsafe fn unsafe_clone(&self) -> Self {
414        Self {
415            // SAFETY: Although we're constructing a `UniqueMmioPointer`, the caller promises that
416            // that this will only be used to read read-only fields. The underlying pointer must be
417            // valid because it came from our `UniqueMmioPointer`.
418            mmio: unsafe {
419                UniqueMmioPointer::new(NonNull::new(self.mmio.ptr().cast_mut()).unwrap())
420            },
421            cam: self.cam,
422        }
423    }
424}
425
426// SAFETY: `&MmioCam` only allows MMIO reads, which are fine to happen concurrently on different CPU
427// cores.
428unsafe impl Sync for MmioCam<'_> {}
429
430/// Information about a PCI Base Address Register.
431#[derive(Clone, Debug, Eq, PartialEq)]
432pub enum BarInfo {
433    /// The BAR is for a memory region.
434    Memory {
435        /// The size of the BAR address and where it can be located.
436        address_type: MemoryBarType,
437        /// If true, then reading from the region doesn't have side effects. The CPU may cache reads
438        /// and merge repeated stores.
439        prefetchable: bool,
440        /// The memory address, always 16-byte aligned.
441        address: u64,
442        /// The size of the BAR in bytes.
443        size: u64,
444    },
445    /// The BAR is for an I/O region.
446    IO {
447        /// The I/O address, always 4-byte aligned.
448        address: u32,
449        /// The size of the BAR in bytes.
450        size: u32,
451    },
452}
453
454impl BarInfo {
455    /// Returns whether this BAR is a 64-bit memory region, and so takes two entries in the table in
456    /// configuration space.
457    pub fn takes_two_entries(&self) -> bool {
458        matches!(
459            self,
460            BarInfo::Memory {
461                address_type: MemoryBarType::Width64,
462                ..
463            }
464        )
465    }
466
467    /// Returns the address and size of this BAR if it is a memory bar, or `None` if it is an IO
468    /// BAR.
469    pub fn memory_address_size(&self) -> Option<(u64, u64)> {
470        if let Self::Memory { address, size, .. } = self {
471            Some((*address, *size))
472        } else {
473            None
474        }
475    }
476}
477
478impl Display for BarInfo {
479    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
480        match self {
481            Self::Memory {
482                address_type,
483                prefetchable,
484                address,
485                size,
486            } => write!(
487                f,
488                "Memory space at {:#010x}, size {}, type {:?}, prefetchable {}",
489                address, size, address_type, prefetchable
490            ),
491            Self::IO { address, size } => {
492                write!(f, "I/O space at {:#010x}, size {}", address, size)
493            }
494        }
495    }
496}
497
498/// The location allowed for a memory BAR.
499#[derive(Copy, Clone, Debug, Eq, PartialEq)]
500pub enum MemoryBarType {
501    /// The BAR has a 32-bit address and can be mapped anywhere in 32-bit address space.
502    Width32,
503    /// The BAR must be mapped below 1MiB.
504    Below1MiB,
505    /// The BAR has a 64-bit address and can be mapped anywhere in 64-bit address space.
506    Width64,
507}
508
509impl From<MemoryBarType> for u8 {
510    fn from(bar_type: MemoryBarType) -> Self {
511        match bar_type {
512            MemoryBarType::Width32 => 0,
513            MemoryBarType::Below1MiB => 1,
514            MemoryBarType::Width64 => 2,
515        }
516    }
517}
518
519impl TryFrom<u8> for MemoryBarType {
520    type Error = PciError;
521
522    fn try_from(value: u8) -> Result<Self, Self::Error> {
523        match value {
524            0 => Ok(Self::Width32),
525            1 => Ok(Self::Below1MiB),
526            2 => Ok(Self::Width64),
527            _ => Err(PciError::InvalidBarType),
528        }
529    }
530}
531
532/// Iterator over capabilities for a device.
533#[derive(Debug)]
534pub struct CapabilityIterator<'a, C: ConfigurationAccess> {
535    configuration_access: &'a C,
536    device_function: DeviceFunction,
537    next_capability_offset: Option<u8>,
538}
539
540impl<C: ConfigurationAccess> Iterator for CapabilityIterator<'_, C> {
541    type Item = CapabilityInfo;
542
543    fn next(&mut self) -> Option<Self::Item> {
544        let offset = self.next_capability_offset?;
545
546        // Read the first 4 bytes of the capability.
547        let capability_header = self
548            .configuration_access
549            .read_word(self.device_function, offset);
550        let id = capability_header as u8;
551        let next_offset = (capability_header >> 8) as u8;
552        let private_header = (capability_header >> 16) as u16;
553
554        self.next_capability_offset = if next_offset == 0 {
555            None
556        } else if next_offset < 64 || next_offset & 0x3 != 0 {
557            warn!("Invalid next capability offset {:#04x}", next_offset);
558            None
559        } else {
560            Some(next_offset)
561        };
562
563        Some(CapabilityInfo {
564            offset,
565            id,
566            private_header,
567        })
568    }
569}
570
571/// Information about a PCI device capability.
572#[derive(Debug, Copy, Clone, Eq, PartialEq)]
573pub struct CapabilityInfo {
574    /// The offset of the capability in the PCI configuration space of the device function.
575    pub offset: u8,
576    /// The ID of the capability.
577    pub id: u8,
578    /// The third and fourth bytes of the capability, to save reading them again.
579    pub private_header: u16,
580}
581
582/// An iterator which enumerates PCI devices and functions on a given bus.
583#[derive(Debug)]
584pub struct BusDeviceIterator<C: ConfigurationAccess> {
585    /// This must only be used to read read-only fields, and must not be exposed outside this
586    /// module, because it uses the same CAM as the main `PciRoot` instance.
587    configuration_access: C,
588    next: DeviceFunction,
589}
590
591impl<C: ConfigurationAccess> Iterator for BusDeviceIterator<C> {
592    type Item = (DeviceFunction, DeviceFunctionInfo);
593
594    fn next(&mut self) -> Option<Self::Item> {
595        while self.next.device < MAX_DEVICES {
596            // Read the header for the current device and function.
597            let current = self.next;
598            let device_vendor = self.configuration_access.read_word(current, 0);
599
600            // Advance to the next device or function.
601            self.next.function += 1;
602            if self.next.function >= MAX_FUNCTIONS {
603                self.next.function = 0;
604                self.next.device += 1;
605            }
606
607            if device_vendor != INVALID_READ {
608                let class_revision = self.configuration_access.read_word(current, 8);
609                let device_id = (device_vendor >> 16) as u16;
610                let vendor_id = device_vendor as u16;
611                let class = (class_revision >> 24) as u8;
612                let subclass = (class_revision >> 16) as u8;
613                let prog_if = (class_revision >> 8) as u8;
614                let revision = class_revision as u8;
615                let bist_type_latency_cache = self.configuration_access.read_word(current, 12);
616                let header_type = HeaderType::from((bist_type_latency_cache >> 16) as u8 & 0x7f);
617                return Some((
618                    current,
619                    DeviceFunctionInfo {
620                        vendor_id,
621                        device_id,
622                        class,
623                        subclass,
624                        prog_if,
625                        revision,
626                        header_type,
627                    },
628                ));
629            }
630        }
631        None
632    }
633}
634
635/// An identifier for a PCI bus, device and function.
636#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
637pub struct DeviceFunction {
638    /// The PCI bus number, between 0 and 255.
639    pub bus: u8,
640    /// The device number on the bus, between 0 and 31.
641    pub device: u8,
642    /// The function number of the device, between 0 and 7.
643    pub function: u8,
644}
645
646impl DeviceFunction {
647    /// Returns whether the device and function numbers are valid, i.e. the device is between 0 and
648    /// 31, and the function is between 0 and 7.
649    pub fn valid(&self) -> bool {
650        self.device < 32 && self.function < 8
651    }
652}
653
654impl Display for DeviceFunction {
655    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
656        write!(f, "{:02x}:{:02x}.{}", self.bus, self.device, self.function)
657    }
658}
659
660/// Information about a PCI device function.
661#[derive(Clone, Debug, Eq, PartialEq)]
662pub struct DeviceFunctionInfo {
663    /// The PCI vendor ID.
664    pub vendor_id: u16,
665    /// The PCI device ID.
666    pub device_id: u16,
667    /// The PCI class.
668    pub class: u8,
669    /// The PCI subclass.
670    pub subclass: u8,
671    /// The PCI programming interface byte.
672    pub prog_if: u8,
673    /// The PCI revision ID.
674    pub revision: u8,
675    /// The type of PCI device.
676    pub header_type: HeaderType,
677}
678
679impl Display for DeviceFunctionInfo {
680    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
681        write!(
682            f,
683            "{:04x}:{:04x} (class {:02x}.{:02x}, rev {:02x}) {:?}",
684            self.vendor_id,
685            self.device_id,
686            self.class,
687            self.subclass,
688            self.revision,
689            self.header_type,
690        )
691    }
692}
693
694/// The type of a PCI device function header.
695#[derive(Copy, Clone, Debug, Eq, PartialEq)]
696pub enum HeaderType {
697    /// A normal PCI device.
698    Standard,
699    /// A PCI to PCI bridge.
700    PciPciBridge,
701    /// A PCI to CardBus bridge.
702    PciCardbusBridge,
703    /// Unrecognised header type.
704    Unrecognised(u8),
705}
706
707impl From<u8> for HeaderType {
708    fn from(value: u8) -> Self {
709        match value {
710            0x00 => Self::Standard,
711            0x01 => Self::PciPciBridge,
712            0x02 => Self::PciCardbusBridge,
713            _ => Self::Unrecognised(value),
714        }
715    }
716}
717
718#[cfg(test)]
719mod tests {
720    use super::*;
721
722    #[test]
723    fn read_status_command() {
724        let status_command = 0x0020_0003;
725        let device_function = DeviceFunction {
726            bus: 0,
727            device: 1,
728            function: 2,
729        };
730        let fake_cam = FakeCam {
731            device_function,
732            bar_values: [0, 1, 4, 0, 0, 0],
733            bar_masks: [
734                0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
735            ],
736            status_command,
737        };
738        let root = PciRoot::new(fake_cam);
739
740        assert_eq!(
741            root.get_status_command(device_function),
742            (
743                Status::MHZ_66_CAPABLE,
744                Command::IO_SPACE | Command::MEMORY_SPACE
745            )
746        );
747    }
748
749    #[test]
750    fn bar_info_unused() {
751        let status_command = 0x0020_0003;
752        let device_function = DeviceFunction {
753            bus: 0,
754            device: 1,
755            function: 2,
756        };
757        let fake_cam = FakeCam {
758            device_function,
759            bar_values: [0, 1, 4, 0, 0, 0],
760            bar_masks: [
761                0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
762            ],
763            status_command,
764        };
765        let fake_cam_orig = fake_cam.clone();
766        let mut root = PciRoot::new(fake_cam);
767
768        assert_eq!(
769            root.bars(device_function).unwrap(),
770            [
771                None,
772                Some(BarInfo::IO {
773                    address: 0,
774                    size: 0,
775                }),
776                Some(BarInfo::Memory {
777                    address_type: MemoryBarType::Width64,
778                    prefetchable: false,
779                    address: 0,
780                    size: 0,
781                }),
782                None,
783                None,
784                None,
785            ]
786        );
787
788        // Status and command should be restored to their initial values, as should BAR values.
789        assert_eq!(root.configuration_access, fake_cam_orig);
790    }
791
792    #[test]
793    fn bar_info_32() {
794        let status_command = 0x0020_0003;
795        let device_function = DeviceFunction {
796            bus: 0,
797            device: 1,
798            function: 2,
799        };
800        let fake_cam = FakeCam {
801            device_function,
802            bar_values: [0b0000, 0b0010, 0b1000, 0b01, 0b0000, 0b0000],
803            bar_masks: [63, 31, 127, 7, 1023, 255],
804            status_command,
805        };
806        let fake_cam_orig = fake_cam.clone();
807        let mut root = PciRoot::new(fake_cam);
808
809        assert_eq!(
810            root.bars(device_function).unwrap(),
811            [
812                Some(BarInfo::Memory {
813                    address_type: MemoryBarType::Width32,
814                    prefetchable: false,
815                    address: 0,
816                    size: 64,
817                }),
818                Some(BarInfo::Memory {
819                    address_type: MemoryBarType::Below1MiB,
820                    prefetchable: false,
821                    address: 0,
822                    size: 32,
823                }),
824                Some(BarInfo::Memory {
825                    address_type: MemoryBarType::Width32,
826                    prefetchable: true,
827                    address: 0,
828                    size: 128,
829                }),
830                Some(BarInfo::IO {
831                    address: 0,
832                    size: 8,
833                }),
834                Some(BarInfo::Memory {
835                    address_type: MemoryBarType::Width32,
836                    prefetchable: false,
837                    address: 0,
838                    size: 1024,
839                }),
840                Some(BarInfo::Memory {
841                    address_type: MemoryBarType::Width32,
842                    prefetchable: false,
843                    address: 0,
844                    size: 256,
845                }),
846            ]
847        );
848
849        // Status and command should be restored to their initial values, as should BAR values.
850        assert_eq!(root.configuration_access, fake_cam_orig);
851    }
852
853    #[test]
854    fn bar_info_64() {
855        let status_command = 0x0020_0003;
856        let device_function = DeviceFunction {
857            bus: 0,
858            device: 1,
859            function: 2,
860        };
861        let fake_cam = FakeCam {
862            device_function,
863            bar_values: [0b0100, 0, 0b0100, 0, 0b1100, 0],
864            bar_masks: [127, 0, 0xffffffff, 3, 255, 0],
865            status_command,
866        };
867        let fake_cam_orig = fake_cam.clone();
868        let mut root = PciRoot::new(fake_cam);
869
870        assert_eq!(
871            root.bars(device_function).unwrap(),
872            [
873                Some(BarInfo::Memory {
874                    address_type: MemoryBarType::Width64,
875                    prefetchable: false,
876                    address: 0,
877                    size: 128,
878                }),
879                None,
880                Some(BarInfo::Memory {
881                    address_type: MemoryBarType::Width64,
882                    prefetchable: false,
883                    address: 0,
884                    size: 0x400000000,
885                }),
886                None,
887                Some(BarInfo::Memory {
888                    address_type: MemoryBarType::Width64,
889                    prefetchable: true,
890                    address: 0,
891                    size: 256,
892                }),
893                None,
894            ]
895        );
896
897        // Status and command should be restored to their initial values, as should BAR values.
898        assert_eq!(root.configuration_access, fake_cam_orig);
899    }
900
901    #[derive(Clone, Debug, Eq, PartialEq)]
902    struct FakeCam {
903        device_function: DeviceFunction,
904        bar_values: [u32; 6],
905        // Bits which can't be changed.
906        bar_masks: [u32; 6],
907        status_command: u32,
908    }
909
910    impl ConfigurationAccess for FakeCam {
911        fn read_word(&self, device_function: DeviceFunction, register_offset: u8) -> u32 {
912            assert_eq!(device_function, self.device_function);
913            assert_eq!(register_offset & 0b11, 0);
914            if register_offset == STATUS_COMMAND_OFFSET {
915                self.status_command
916            } else if register_offset >= BAR0_OFFSET && register_offset < 0x28 {
917                let bar_index = usize::from((register_offset - BAR0_OFFSET) / 4);
918                self.bar_values[bar_index]
919            } else {
920                println!("Reading unsupported register offset {}", register_offset);
921                0xffffffff
922            }
923        }
924
925        fn write_word(&mut self, device_function: DeviceFunction, register_offset: u8, data: u32) {
926            assert_eq!(device_function, self.device_function);
927            assert_eq!(register_offset & 0b11, 0);
928            if register_offset == STATUS_COMMAND_OFFSET {
929                // Ignore write to status, only write to command.
930                self.status_command = (self.status_command & 0xffff_0000) | (data & 0x0000_ffff);
931            } else if register_offset >= BAR0_OFFSET && register_offset < 0x28 {
932                let bar_index = usize::from((register_offset - BAR0_OFFSET) / 4);
933                let bar_mask = self.bar_masks[bar_index];
934                self.bar_values[bar_index] =
935                    (bar_mask & self.bar_values[bar_index]) | (!bar_mask & data);
936            } else {
937                println!("Ignoring write of {:#010x} to {}", data, register_offset);
938                return;
939            }
940        }
941
942        unsafe fn unsafe_clone(&self) -> Self {
943            self.clone()
944        }
945    }
946}