pci_types/
lib.rs

1#![no_std]
2
3pub mod capability;
4pub mod device_type;
5mod register;
6
7pub use register::{CommandRegister, DevselTiming, StatusRegister};
8
9use crate::capability::CapabilityIterator;
10use bit_field::BitField;
11use core::fmt;
12
13/// The address of a PCIe function.
14///
15/// PCIe supports 65536 segments, each with 256 buses, each with 32 slots, each with 8 possible functions. We pack this into a `u32`:
16///
17/// ```ignore
18/// 32                              16               8         3      0
19///  +-------------------------------+---------------+---------+------+
20///  |            segment            |      bus      | device  | func |
21///  +-------------------------------+---------------+---------+------+
22/// ```
23#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
24pub struct PciAddress(u32);
25
26impl PciAddress {
27    pub fn new(segment: u16, bus: u8, device: u8, function: u8) -> PciAddress {
28        let mut result = 0;
29        result.set_bits(0..3, function as u32);
30        result.set_bits(3..8, device as u32);
31        result.set_bits(8..16, bus as u32);
32        result.set_bits(16..32, segment as u32);
33        PciAddress(result)
34    }
35
36    pub fn segment(&self) -> u16 {
37        self.0.get_bits(16..32) as u16
38    }
39
40    pub fn bus(&self) -> u8 {
41        self.0.get_bits(8..16) as u8
42    }
43
44    pub fn device(&self) -> u8 {
45        self.0.get_bits(3..8) as u8
46    }
47
48    pub fn function(&self) -> u8 {
49        self.0.get_bits(0..3) as u8
50    }
51}
52
53impl fmt::Display for PciAddress {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        write!(f, "{:02x}-{:02x}:{:02x}.{}", self.segment(), self.bus(), self.device(), self.function())
56    }
57}
58
59impl fmt::Debug for PciAddress {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        write!(f, "{}", self)
62    }
63}
64
65pub type VendorId = u16;
66pub type DeviceId = u16;
67pub type DeviceRevision = u8;
68pub type BaseClass = u8;
69pub type SubClass = u8;
70pub type Interface = u8;
71pub type SubsystemId = u16;
72pub type SubsystemVendorId = u16;
73pub type InterruptLine = u8;
74pub type InterruptPin = u8;
75
76// TODO: documentation
77pub trait ConfigRegionAccess {
78    /// Performs a PCI read at `address` with `offset`.
79    ///
80    /// # Safety
81    ///
82    /// `address` and `offset` must be valid for PCI reads.
83    unsafe fn read(&self, address: PciAddress, offset: u16) -> u32;
84
85    /// Performs a PCI write at `address` with `offset`.
86    ///
87    /// # Safety
88    ///
89    /// `address` and `offset` must be valid for PCI writes.
90    unsafe fn write(&self, address: PciAddress, offset: u16, value: u32);
91}
92
93impl<T: ConfigRegionAccess + ?Sized> ConfigRegionAccess for &T {
94    #[inline]
95    unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 {
96        (**self).read(address, offset)
97    }
98
99    #[inline]
100    unsafe fn write(&self, address: PciAddress, offset: u16, value: u32) {
101        (**self).write(address, offset, value)
102    }
103}
104
105#[non_exhaustive]
106#[derive(Clone, Copy, PartialEq, Eq, Debug)]
107pub enum HeaderType {
108    Endpoint,
109    PciPciBridge,
110    CardBusBridge,
111    Unknown(u8),
112}
113
114/// Every PCI configuration region starts with a header made up of two parts:
115///    - a predefined region that identify the function (bytes `0x00..0x10`)
116///    - a device-dependent region that depends on the Header Type field
117///
118/// The predefined region is of the form:
119/// ```ignore
120///     32                            16                              0
121///      +-----------------------------+------------------------------+
122///      |       Device ID             |       Vendor ID              | 0x00
123///      |                             |                              |
124///      +-----------------------------+------------------------------+
125///      |         Status              |       Command                | 0x04
126///      |                             |                              |
127///      +-----------------------------+---------------+--------------+
128///      |               Class Code                    |   Revision   | 0x08
129///      |                                             |      ID      |
130///      +--------------+--------------+---------------+--------------+
131///      |     BIST     |    Header    |    Latency    |  Cacheline   | 0x0c
132///      |              |     type     |     timer     |    size      |
133///      +--------------+--------------+---------------+--------------+
134/// ```
135pub struct PciHeader(PciAddress);
136
137impl PciHeader {
138    pub fn new(address: PciAddress) -> PciHeader {
139        PciHeader(address)
140    }
141
142    pub fn address(&self) -> PciAddress {
143        self.0
144    }
145
146    pub fn id(&self, access: impl ConfigRegionAccess) -> (VendorId, DeviceId) {
147        let id = unsafe { access.read(self.0, 0x00) };
148        (id.get_bits(0..16) as VendorId, id.get_bits(16..32) as DeviceId)
149    }
150
151    pub fn header_type(&self, access: impl ConfigRegionAccess) -> HeaderType {
152        /*
153         * Read bits 0..=6 of the Header Type. Bit 7 dictates whether the device has multiple functions and so
154         * isn't returned here.
155         */
156        match unsafe { access.read(self.0, 0x0c) }.get_bits(16..23) {
157            0x00 => HeaderType::Endpoint,
158            0x01 => HeaderType::PciPciBridge,
159            0x02 => HeaderType::CardBusBridge,
160            t => HeaderType::Unknown(t as u8),
161        }
162    }
163
164    pub fn has_multiple_functions(&self, access: impl ConfigRegionAccess) -> bool {
165        /*
166         * Reads bit 7 of the Header Type, which is 1 if the device has multiple functions.
167         */
168        unsafe { access.read(self.0, 0x0c) }.get_bit(23)
169    }
170
171    pub fn revision_and_class(
172        &self,
173        access: impl ConfigRegionAccess,
174    ) -> (DeviceRevision, BaseClass, SubClass, Interface) {
175        let field = unsafe { access.read(self.0, 0x08) };
176        (
177            field.get_bits(0..8) as DeviceRevision,
178            field.get_bits(24..32) as BaseClass,
179            field.get_bits(16..24) as SubClass,
180            field.get_bits(8..16) as Interface,
181        )
182    }
183
184    pub fn status(&self, access: impl ConfigRegionAccess) -> StatusRegister {
185        let data = unsafe { access.read(self.0, 0x4).get_bits(16..32) };
186        StatusRegister::new(data as u16)
187    }
188
189    pub fn command(&self, access: impl ConfigRegionAccess) -> CommandRegister {
190        let data = unsafe { access.read(self.0, 0x4).get_bits(0..16) };
191        CommandRegister::from_bits_retain(data as u16)
192    }
193
194    pub fn update_command<F>(&mut self, access: impl ConfigRegionAccess, f: F)
195    where
196        F: FnOnce(CommandRegister) -> CommandRegister,
197    {
198        let mut data = unsafe { access.read(self.0, 0x4) };
199        let new_command = f(CommandRegister::from_bits_retain(data.get_bits(0..16) as u16));
200        data.set_bits(0..16, new_command.bits() as u32);
201        unsafe {
202            access.write(self.0, 0x4, data);
203        }
204    }
205}
206
207/// Endpoints have a Type-0 header, so the remainder of the header is of the form:
208/// ```ignore
209///     32                           16                              0
210///     +-----------------------------------------------------------+ 0x00
211///     |                                                           |
212///     |                Predefined region of header                |
213///     |                                                           |
214///     |                                                           |
215///     +-----------------------------------------------------------+
216///     |                  Base Address Register 0                  | 0x10
217///     |                                                           |
218///     +-----------------------------------------------------------+
219///     |                  Base Address Register 1                  | 0x14
220///     |                                                           |
221///     +-----------------------------------------------------------+
222///     |                  Base Address Register 2                  | 0x18
223///     |                                                           |
224///     +-----------------------------------------------------------+
225///     |                  Base Address Register 3                  | 0x1c
226///     |                                                           |
227///     +-----------------------------------------------------------+
228///     |                  Base Address Register 4                  | 0x20
229///     |                                                           |
230///     +-----------------------------------------------------------+
231///     |                  Base Address Register 5                  | 0x24
232///     |                                                           |
233///     +-----------------------------------------------------------+
234///     |                  CardBus CIS Pointer                      | 0x28
235///     |                                                           |
236///     +----------------------------+------------------------------+
237///     |       Subsystem ID         |    Subsystem vendor ID       | 0x2c
238///     |                            |                              |
239///     +----------------------------+------------------------------+
240///     |               Expansion ROM Base Address                  | 0x30
241///     |                                                           |
242///     +--------------------------------------------+--------------+
243///     |                 Reserved                   | Capabilities | 0x34
244///     |                                            |   Pointer    |
245///     +--------------------------------------------+--------------+
246///     |                         Reserved                          | 0x38
247///     |                                                           |
248///     +--------------+--------------+--------------+--------------+
249///     |   Max_Lat    |   Min_Gnt    |  Interrupt   |  Interrupt   | 0x3c
250///     |              |              |   pin        |   line       |
251///     +--------------+--------------+--------------+--------------+
252/// ```
253pub struct EndpointHeader(PciAddress);
254
255impl EndpointHeader {
256    pub fn from_header(header: PciHeader, access: impl ConfigRegionAccess) -> Option<EndpointHeader> {
257        match header.header_type(access) {
258            HeaderType::Endpoint => Some(EndpointHeader(header.0)),
259            _ => None,
260        }
261    }
262
263    pub fn header(&self) -> PciHeader {
264        PciHeader(self.0)
265    }
266
267    pub fn status(&self, access: impl ConfigRegionAccess) -> StatusRegister {
268        self.header().status(access)
269    }
270
271    pub fn command(&self, access: impl ConfigRegionAccess) -> CommandRegister {
272        self.header().command(access)
273    }
274
275    pub fn update_command<F>(&mut self, access: impl ConfigRegionAccess, f: F)
276    where
277        F: FnOnce(CommandRegister) -> CommandRegister,
278    {
279        self.header().update_command(access, f);
280    }
281
282    pub fn capability_pointer(&self, access: impl ConfigRegionAccess) -> u16 {
283        let status = self.status(&access);
284        if status.has_capability_list() {
285            unsafe { access.read(self.0, 0x34).get_bits(0..8) as u16 }
286        } else {
287            0
288        }
289    }
290
291    pub fn capabilities<T: ConfigRegionAccess>(&self, access: T) -> CapabilityIterator<T> {
292        let pointer = self.capability_pointer(&access);
293        CapabilityIterator::new(self.0, pointer, access)
294    }
295
296    pub fn subsystem(&self, access: impl ConfigRegionAccess) -> (SubsystemId, SubsystemVendorId) {
297        let data = unsafe { access.read(self.0, 0x2c) };
298        (data.get_bits(16..32) as u16, data.get_bits(0..16) as u16)
299    }
300
301    /// Get the contents of a BAR in a given slot. Empty bars will return `None`.
302    ///
303    /// ### Note
304    /// 64-bit memory BARs use two slots, so if one is decoded in e.g. slot #0, this method should not be called
305    /// for slot #1
306    pub fn bar(&self, slot: u8, access: impl ConfigRegionAccess) -> Option<Bar> {
307        if slot >= 6 {
308            return None;
309        }
310
311        let offset = 0x10 + (slot as u16) * 4;
312        let bar = unsafe { access.read(self.0, offset) };
313
314        /*
315         * If bit 0 is `0`, the BAR is in memory. If it's `1`, it's in I/O.
316         */
317        if !bar.get_bit(0) {
318            let prefetchable = bar.get_bit(3);
319            let address = bar.get_bits(4..32) << 4;
320
321            match bar.get_bits(1..3) {
322                0b00 => {
323                    let size = unsafe {
324                        access.write(self.0, offset, 0xfffffff0);
325                        let mut readback = access.read(self.0, offset);
326                        access.write(self.0, offset, address);
327
328                        /*
329                         * If the entire readback value is zero, the BAR is not implemented, so we return `None`.
330                         */
331                        if readback == 0x0 {
332                            return None;
333                        }
334
335                        readback.set_bits(0..4, 0);
336                        1 << readback.trailing_zeros()
337                    };
338                    Some(Bar::Memory32 { address, size, prefetchable })
339                }
340
341                0b10 => {
342                    /*
343                     * If the BAR is 64 bit-wide and this slot is the last, there is no second slot to read.
344                     */
345                    if slot >= 5 {
346                        return None;
347                    }
348
349                    let address_upper = unsafe { access.read(self.0, offset + 4) };
350
351                    let size = unsafe {
352                        access.write(self.0, offset, 0xfffffff0);
353                        access.write(self.0, offset + 4, 0xffffffff);
354                        let mut readback_low = access.read(self.0, offset);
355                        let readback_high = access.read(self.0, offset + 4);
356                        access.write(self.0, offset, address);
357                        access.write(self.0, offset + 4, address_upper);
358
359                        /*
360                         * If the readback from the first slot is not 0, the size of the BAR is less than 4GiB.
361                         */
362                        readback_low.set_bits(0..4, 0);
363                        if readback_low != 0 {
364                            (1 << readback_low.trailing_zeros()) as u64
365                        } else {
366                            1u64 << ((readback_high.trailing_zeros() + 32) as u64)
367                        }
368                    };
369
370                    let address = {
371                        let mut address = address as u64;
372                        // TODO: do we need to mask off the lower bits on this?
373                        address.set_bits(32..64, address_upper as u64);
374                        address
375                    };
376
377                    Some(Bar::Memory64 { address, size, prefetchable })
378                }
379                // TODO: should we bother to return an error here?
380                _ => panic!("BAR Memory type is reserved!"),
381            }
382        } else {
383            Some(Bar::Io { port: bar.get_bits(2..32) << 2 })
384        }
385    }
386
387    /// Write to a BAR, setting the address for a device to use.
388    ///
389    /// # Safety
390    ///
391    /// The supplied value must be a valid BAR value (refer to the PCIe specification for
392    /// requirements) and must be of the correct size (i.e. no larger than `u32::MAX` for 32-bit
393    /// BARs). In the case of a 64-bit BAR, the supplied slot should be the first slot of the pair.
394    pub unsafe fn write_bar(
395        &mut self,
396        slot: u8,
397        access: impl ConfigRegionAccess,
398        value: usize,
399    ) -> Result<(), BarWriteError> {
400        match self.bar(slot, &access) {
401            Some(Bar::Memory64 { .. }) => {
402                let offset = 0x10 + (slot as u16) * 4;
403                unsafe {
404                    access.write(self.0, offset, value.get_bits(0..32) as u32);
405                    access.write(self.0, offset + 4, value.get_bits(32..64) as u32);
406                }
407                Ok(())
408            }
409            Some(Bar::Memory32 { .. }) | Some(Bar::Io { .. }) => {
410                if value > u32::MAX as usize {
411                    return Err(BarWriteError::InvalidValue);
412                }
413
414                let offset = 0x10 + (slot as u16) * 4;
415                unsafe {
416                    access.write(self.0, offset, value as u32);
417                }
418                Ok(())
419            }
420            None => Err(BarWriteError::NoSuchBar),
421        }
422    }
423
424    pub fn interrupt(&self, access: impl ConfigRegionAccess) -> (InterruptPin, InterruptLine) {
425        // According to the PCI Express Specification 4.0, Min_Gnt/Max_Lat registers
426        // must be read-only and hardwired to 00h.
427        let data = unsafe { access.read(self.0, 0x3c) };
428        (data.get_bits(8..16) as u8, data.get_bits(0..8) as u8)
429    }
430
431    pub fn update_interrupt<F>(&mut self, access: impl ConfigRegionAccess, f: F)
432    where
433        F: FnOnce((InterruptPin, InterruptLine)) -> (InterruptPin, InterruptLine),
434    {
435        let mut data = unsafe { access.read(self.0, 0x3c) };
436        let (new_pin, new_line) = f((data.get_bits(8..16) as u8, data.get_bits(0..8) as u8));
437        data.set_bits(8..16, new_pin.into());
438        data.set_bits(0..8, new_line.into());
439        unsafe {
440            access.write(self.0, 0x3c, data);
441        }
442    }
443}
444
445/// PCI-PCI Bridges have a Type-1 header, so the remainder of the header is of the form:
446/// ```ignore
447///     32                           16                              0
448///     +-----------------------------------------------------------+ 0x00
449///     |                                                           |
450///     |                Predefined region of header                |
451///     |                                                           |
452///     |                                                           |
453///     +-----------------------------------------------------------+
454///     |                  Base Address Register 0                  | 0x10
455///     |                                                           |
456///     +-----------------------------------------------------------+
457///     |                  Base Address Register 1                  | 0x14
458///     |                                                           |
459///     +--------------+--------------+--------------+--------------+
460///     | Secondary    | Subordinate  |  Secondary   | Primary Bus  | 0x18
461///     |Latency Timer | Bus Number   |  Bus Number  |   Number     |
462///     +--------------+--------------+--------------+--------------+
463///     |      Secondary Status       |  I/O Limit   |   I/O Base   | 0x1C
464///     |                             |              |              |
465///     +-----------------------------+--------------+--------------+
466///     |        Memory Limit         |         Memory Base         | 0x20
467///     |                             |                             |
468///     +-----------------------------+-----------------------------+
469///     |  Prefetchable Memory Limit  |  Prefetchable Memory Base   | 0x24
470///     |                             |                             |
471///     +-----------------------------+-----------------------------+
472///     |             Prefetchable Base Upper 32 Bits               | 0x28
473///     |                                                           |
474///     +-----------------------------------------------------------+
475///     |             Prefetchable Limit Upper 32 Bits              | 0x2C
476///     |                                                           |
477///     +-----------------------------+-----------------------------+
478///     |   I/O Limit Upper 16 Bits   |   I/O Base Upper 16 Bits    | 0x30
479///     |                             |                             |
480///     +-----------------------------+--------------+--------------+
481///     |              Reserved                      |  Capability  | 0x34
482///     |                                            |   Pointer    |
483///     +--------------------------------------------+--------------+
484///     |                  Expansion ROM base address               | 0x38
485///     |                                                           |
486///     +-----------------------------+--------------+--------------+
487///     |    Bridge Control           |  Interrupt   | Interrupt    | 0x3C
488///     |                             |     PIN      |   Line       |
489///     +-----------------------------+--------------+--------------+
490/// ```
491pub struct PciPciBridgeHeader(PciAddress);
492
493impl PciPciBridgeHeader {
494    pub fn from_header(header: PciHeader, access: impl ConfigRegionAccess) -> Option<PciPciBridgeHeader> {
495        match header.header_type(access) {
496            HeaderType::PciPciBridge => Some(PciPciBridgeHeader(header.0)),
497            _ => None,
498        }
499    }
500
501    pub fn header(&self) -> PciHeader {
502        PciHeader(self.0)
503    }
504
505    pub fn status(&self, access: impl ConfigRegionAccess) -> StatusRegister {
506        self.header().status(access)
507    }
508
509    pub fn command(&self, access: impl ConfigRegionAccess) -> CommandRegister {
510        self.header().command(access)
511    }
512
513    pub fn update_command<F>(&mut self, access: impl ConfigRegionAccess, f: F)
514    where
515        F: FnOnce(CommandRegister) -> CommandRegister,
516    {
517        self.header().update_command(access, f);
518    }
519
520    pub fn primary_bus_number(&self, access: impl ConfigRegionAccess) -> u8 {
521        let data = unsafe { access.read(self.0, 0x18).get_bits(0..8) };
522        data as u8
523    }
524
525    pub fn secondary_bus_number(&self, access: impl ConfigRegionAccess) -> u8 {
526        let data = unsafe { access.read(self.0, 0x18).get_bits(8..16) };
527        data as u8
528    }
529
530    pub fn subordinate_bus_number(&self, access: impl ConfigRegionAccess) -> u8 {
531        let data = unsafe { access.read(self.0, 0x18).get_bits(16..24) };
532        data as u8
533    }
534}
535
536pub const MAX_BARS: usize = 6;
537
538#[derive(Clone, Copy, Debug)]
539pub enum Bar {
540    Memory32 { address: u32, size: u32, prefetchable: bool },
541    Memory64 { address: u64, size: u64, prefetchable: bool },
542    Io { port: u32 },
543}
544
545impl Bar {
546    /// Return the IO port of this BAR or panic if not an IO BAR.
547    pub fn unwrap_io(self) -> u32 {
548        match self {
549            Bar::Io { port } => port,
550            Bar::Memory32 { .. } | Bar::Memory64 { .. } => panic!("expected IO BAR, found memory BAR"),
551        }
552    }
553
554    /// Return the address and size of this BAR or panic if not a memory BAR.
555    pub fn unwrap_mem(self) -> (usize, usize) {
556        match self {
557            Bar::Memory32 { address, size, prefetchable: _ } => (address as usize, size as usize),
558            Bar::Memory64 { address, size, prefetchable: _ } => (
559                address.try_into().expect("conversion from 64bit BAR to usize failed"),
560                size.try_into().expect("conversion from 64bit BAR to usize failed"),
561            ),
562            Bar::Io { .. } => panic!("expected memory BAR, found IO BAR"),
563        }
564    }
565}
566
567#[derive(Clone, Copy, PartialEq, Eq, Debug)]
568pub enum BarWriteError {
569    NoSuchBar,
570    InvalidValue,
571}