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