pci_core/
spec.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Types and constants specified by the PCI spec.
5//!
6//! This module MUST NOT contain any vendor-specific constants!
7
8pub mod hwid {
9    //! Hardware ID types and constants
10
11    #![expect(missing_docs)] // constants/fields are self-explanatory
12
13    use core::fmt;
14    use inspect::Inspect;
15
16    /// A collection of hard-coded hardware IDs specific to a particular PCI
17    /// device, as reflected in their corresponding PCI configuration space
18    /// registers.
19    ///
20    /// See PCI 2.3 Spec - 6.2.1 for details on each of these fields.
21    #[derive(Debug, Copy, Clone, Inspect)]
22    pub struct HardwareIds {
23        #[inspect(hex)]
24        pub vendor_id: u16,
25        #[inspect(hex)]
26        pub device_id: u16,
27        #[inspect(hex)]
28        pub revision_id: u8,
29        pub prog_if: ProgrammingInterface,
30        pub sub_class: Subclass,
31        pub base_class: ClassCode,
32        // TODO: this struct should be re-jigged when adding support for other
33        // header types (e.g: type 1)
34        #[inspect(hex)]
35        pub type0_sub_vendor_id: u16,
36        #[inspect(hex)]
37        pub type0_sub_system_id: u16,
38    }
39
40    open_enum::open_enum! {
41        /// ClassCode identifies the PCI device's type.
42        ///
43        /// Values pulled from <https://wiki.osdev.org/PCI#Class_Codes>.
44        #[derive(Inspect)]
45        #[inspect(display)]
46        pub enum ClassCode: u8 {
47            UNCLASSIFIED = 0x00,
48            MASS_STORAGE_CONTROLLER = 0x01,
49            NETWORK_CONTROLLER = 0x02,
50            DISPLAY_CONTROLLER = 0x03,
51            MULTIMEDIA_CONTROLLER = 0x04,
52            MEMORY_CONTROLLER = 0x05,
53            BRIDGE = 0x06,
54            SIMPLE_COMMUNICATION_CONTROLLER = 0x07,
55            BASE_SYSTEM_PERIPHERAL = 0x08,
56            INPUT_DEVICE_CONTROLLER = 0x09,
57            DOCKING_STATION = 0x0A,
58            PROCESSOR = 0x0B,
59            SERIAL_BUS_CONTROLLER = 0x0C,
60            WIRELESS_CONTROLLER = 0x0D,
61            INTELLIGENT_CONTROLLER = 0x0E,
62            SATELLITE_COMMUNICATION_CONTROLLER = 0x0F,
63            ENCRYPTION_CONTROLLER = 0x10,
64            SIGNAL_PROCESSING_CONTROLLER = 0x11,
65            PROCESSING_ACCELERATOR = 0x12,
66            NONESSENTIAL_INSTRUMENTATION = 0x13,
67            // 0x14 - 0x3F: Reserved
68            CO_PROCESSOR = 0x40,
69            // 0x41 - 0xFE: Reserved
70            /// Vendor specific
71            UNASSIGNED = 0xFF,
72        }
73    }
74
75    impl ClassCode {
76        pub fn is_reserved(&self) -> bool {
77            let c = &self.0;
78            (0x14..=0x3f).contains(c) || (0x41..=0xfe).contains(c)
79        }
80    }
81
82    impl fmt::Display for ClassCode {
83        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84            if self.is_reserved() {
85                return write!(f, "RESERVED({:#04x})", self.0);
86            }
87            fmt::Debug::fmt(self, f)
88        }
89    }
90
91    impl From<u8> for ClassCode {
92        fn from(c: u8) -> Self {
93            Self(c)
94        }
95    }
96
97    impl From<ClassCode> for u8 {
98        fn from(c: ClassCode) -> Self {
99            c.0
100        }
101    }
102
103    // Most subclass/programming interface values aren't used, and don't have names that can easily be made into variable
104    // identifiers (eg, "ISA Compatibility mode controller, supports both channels switched to PCI native mode, supports bus mastering").
105    //
106    // Therefore, only add values as needed.
107
108    open_enum::open_enum! {
109        /// SubclassCode identifies the PCI device's function.
110        ///
111        /// Values pulled from <https://wiki.osdev.org/PCI#Class_Codes>.
112        #[derive(Inspect)]
113        #[inspect(transparent(hex))]
114        pub enum Subclass: u8 {
115            // TODO: As more values are used, add them here.
116
117            NONE = 0x00,
118
119            // Mass Storage Controller (Class code: 0x01)
120            MASS_STORAGE_CONTROLLER_NON_VOLATILE_MEMORY = 0x08,
121
122            // Network Controller (Class code: 0x02)
123            // Other values: 0x01 - 0x08, 0x80
124            NETWORK_CONTROLLER_ETHERNET = 0x00,
125
126            // Bridge (Class code: 0x06)
127            // Other values: 0x02 - 0x0A
128            BRIDGE_HOST = 0x00,
129            BRIDGE_ISA = 0x01,
130            BRIDGE_PCI_TO_PCI = 0x04,
131            BRIDGE_OTHER = 0x80,
132
133            // Base System Peripheral (Class code: 0x08)
134            // Other values: 0x00 - 0x06
135            BASE_SYSTEM_PERIPHERAL_OTHER = 0x80,
136        }
137    }
138
139    impl From<u8> for Subclass {
140        fn from(c: u8) -> Self {
141            Self(c)
142        }
143    }
144
145    impl From<Subclass> for u8 {
146        fn from(c: Subclass) -> Self {
147            c.0
148        }
149    }
150
151    open_enum::open_enum! {
152        /// ProgrammingInterface (aka, program interface byte) identifies the PCI device's
153        /// register-level programming interface.
154        ///
155        /// Values pulled from <https://wiki.osdev.org/PCI#Class_Codes>.
156        #[derive(Inspect)]
157        #[inspect(transparent(hex))]
158        pub enum ProgrammingInterface: u8{
159            // TODO: As more values are used, add them here.
160
161            NONE = 0x00,
162
163            // Non-Volatile Memory Controller (Class code:0x01, Subclass: 0x08)
164            // Other values: 0x01
165            MASS_STORAGE_CONTROLLER_NON_VOLATILE_MEMORY_NVME = 0x02,
166
167            // Ethernet Controller (Class code: 0x02, Subclass: 0x00)
168            NETWORK_CONTROLLER_ETHERNET_GDMA = 0x01,
169        }
170    }
171
172    impl From<u8> for ProgrammingInterface {
173        fn from(c: u8) -> Self {
174            Self(c)
175        }
176    }
177
178    impl From<ProgrammingInterface> for u8 {
179        fn from(c: ProgrammingInterface) -> Self {
180            c.0
181        }
182    }
183}
184
185/// Configuration Space
186///
187/// Sources: PCI 2.3 Spec - Chapter 6
188#[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
189pub mod cfg_space {
190    use bitfield_struct::bitfield;
191    use inspect::Inspect;
192    use zerocopy::FromBytes;
193    use zerocopy::Immutable;
194    use zerocopy::IntoBytes;
195    use zerocopy::KnownLayout;
196
197    open_enum::open_enum! {
198        /// Offsets into the type 00h configuration space header.
199        ///
200        /// Table pulled from <https://wiki.osdev.org/PCI>
201        ///
202        /// | Offset | Bits 31-24                 | Bits 23-16  | Bits 15-8           | Bits 7-0             |
203        /// |--------|----------------------------|-------------|---------------------|--------------------- |
204        /// | 0x0    | Device ID                  |             | Vendor ID           |                      |
205        /// | 0x4    | Status                     |             | Command             |                      |
206        /// | 0x8    | Class code                 |             |                     | Revision ID          |
207        /// | 0xC    | BIST                       | Header type | Latency Timer       | Cache Line Size      |
208        /// | 0x10   | Base address #0 (BAR0)     |             |                     |                      |
209        /// | 0x14   | Base address #1 (BAR1)     |             |                     |                      |
210        /// | 0x18   | Base address #2 (BAR2)     |             |                     |                      |
211        /// | 0x1C   | Base address #3 (BAR3)     |             |                     |                      |
212        /// | 0x20   | Base address #4 (BAR4)     |             |                     |                      |
213        /// | 0x24   | Base address #5 (BAR5)     |             |                     |                      |
214        /// | 0x28   | Cardbus CIS Pointer        |             |                     |                      |
215        /// | 0x2C   | Subsystem ID               |             | Subsystem Vendor ID |                      |
216        /// | 0x30   | Expansion ROM base address |             |                     |                      |
217        /// | 0x34   | Reserved                   |             |                     | Capabilities Pointer |
218        /// | 0x38   | Reserved                   |             |                     |                      |
219        /// | 0x3C   | Max latency                | Min Grant   | Interrupt PIN       | Interrupt Line       |
220        pub enum HeaderType00: u16 {
221            DEVICE_VENDOR      = 0x00,
222            STATUS_COMMAND     = 0x04,
223            CLASS_REVISION     = 0x08,
224            BIST_HEADER        = 0x0C,
225            BAR0               = 0x10,
226            BAR1               = 0x14,
227            BAR2               = 0x18,
228            BAR3               = 0x1C,
229            BAR4               = 0x20,
230            BAR5               = 0x24,
231            CARDBUS_CIS_PTR    = 0x28,
232            SUBSYSTEM_ID       = 0x2C,
233            EXPANSION_ROM_BASE = 0x30,
234            RESERVED_CAP_PTR   = 0x34,
235            RESERVED           = 0x38,
236            LATENCY_INTERRUPT  = 0x3C,
237        }
238    }
239
240    pub const HEADER_TYPE_00_SIZE: u16 = 0x40;
241
242    open_enum::open_enum! {
243        /// Offsets into the type 01h configuration space header.
244        ///
245        /// Table pulled from <https://wiki.osdev.org/PCI>
246        ///
247        /// | Offset | Bits 31-24                       | Bits 23-16             | Bits 15-8                | Bits 7-0             |
248        /// |--------|----------------------------------|------------------------|--------------------------|--------------------- |
249        /// | 0x0    | Device ID                        |                        | Vendor ID                |                      |
250        /// | 0x4    | Status                           |                        | Command                  |                      |
251        /// | 0x8    | Class code                       |                        |                          | Revision ID          |
252        /// | 0xC    | BIST                             | Header Type            | Latency Timer            | Cache Line Size      |
253        /// | 0x10   | Base address #0 (BAR0)           |                        |                          |                      |
254        /// | 0x14   | Base address #1 (BAR1)           |                        |                          |                      |
255        /// | 0x18   | Secondary Latency Timer          | Subordinate Bus Number | Secondary Bus Number     | Primary Bus Number   |
256        /// | 0x1C   | Secondary Status                 |                        | I/O Limit                | I/O Base             |
257        /// | 0x20   | Memory Limit                     |                        | Memory Base              |                      |
258        /// | 0x24   | Prefetchable Memory Limit        |                        | Prefetchable Memory Base |                      |
259        /// | 0x28   | Prefetchable Base Upper 32 Bits  |                        |                          |                      |
260        /// | 0x2C   | Prefetchable Limit Upper 32 Bits |                        |                          |                      |
261        /// | 0x30   | I/O Limit Upper 16 Bits          |                        | I/O Base Upper 16 Bits   |                      |
262        /// | 0x34   | Reserved                         |                        |                          | Capabilities Pointer |
263        /// | 0x38   | Expansion ROM Base Address       |                        |                          |                      |
264        /// | 0x3C   | Bridge Control                   |                        | Interrupt PIN            | Interrupt Line       |
265        pub enum HeaderType01: u16 {
266            DEVICE_VENDOR         = 0x00,
267            STATUS_COMMAND        = 0x04,
268            CLASS_REVISION        = 0x08,
269            BIST_HEADER           = 0x0C,
270            BAR0                  = 0x10,
271            BAR1                  = 0x14,
272            LATENCY_BUS_NUMBERS   = 0x18,
273            SEC_STATUS_IO_RANGE   = 0x1C,
274            MEMORY_RANGE          = 0x20,
275            PREFETCH_RANGE        = 0x24,
276            PREFETCH_BASE_UPPER   = 0x28,
277            PREFETCH_LIMIT_UPPER  = 0x2C,
278            IO_RANGE_UPPER        = 0x30,
279            RESERVED_CAP_PTR      = 0x34,
280            EXPANSION_ROM_BASE    = 0x38,
281            BRDIGE_CTRL_INTERRUPT = 0x3C,
282        }
283    }
284
285    pub const HEADER_TYPE_01_SIZE: u16 = 0x40;
286
287    /// BAR in-band encoding bits.
288    ///
289    /// The low bits of the BAR are not actually part of the address.
290    /// Instead, they are used to in-band encode various bits of
291    /// metadata about the BAR, and are masked off when determining the
292    /// actual address.
293    #[bitfield(u32)]
294    pub struct BarEncodingBits {
295        pub use_pio: bool,
296
297        _reserved: bool,
298
299        /// False indicates 32 bit.
300        /// Only used in MMIO
301        pub type_64_bit: bool,
302        pub prefetchable: bool,
303
304        #[bits(28)]
305        _reserved2: u32,
306    }
307
308    /// Command Register
309    #[derive(Inspect)]
310    #[bitfield(u16)]
311    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
312    pub struct Command {
313        pub pio_enabled: bool,
314        pub mmio_enabled: bool,
315        pub bus_master: bool,
316        pub special_cycles: bool,
317        pub enable_memory_write_invalidate: bool,
318        pub vga_palette_snoop: bool,
319        pub parity_error_response: bool,
320        /// must be 0
321        #[bits(1)]
322        _reserved: u16,
323        pub enable_serr: bool,
324        pub enable_fast_b2b: bool,
325        pub intx_disable: bool,
326        #[bits(5)]
327        _reserved2: u16,
328    }
329
330    /// Status Register
331    #[bitfield(u16)]
332    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
333    pub struct Status {
334        #[bits(3)]
335        _reserved: u16,
336        pub interrupt_status: bool,
337        pub capabilities_list: bool,
338        pub capable_mhz_66: bool,
339        _reserved2: bool,
340        pub capable_fast_b2b: bool,
341        pub err_master_parity: bool,
342
343        #[bits(2)]
344        pub devsel: DevSel,
345
346        pub abort_target_signaled: bool,
347        pub abort_target_received: bool,
348        pub abort_master_received: bool,
349        pub err_signaled: bool,
350        pub err_detected_parity: bool,
351    }
352
353    #[derive(Debug)]
354    #[repr(u16)]
355    pub enum DevSel {
356        Fast = 0b00,
357        Medium = 0b01,
358        Slow = 0b10,
359    }
360
361    impl DevSel {
362        const fn from_bits(bits: u16) -> Self {
363            match bits {
364                0b00 => DevSel::Fast,
365                0b01 => DevSel::Medium,
366                0b10 => DevSel::Slow,
367                _ => unreachable!(),
368            }
369        }
370
371        const fn into_bits(self) -> u16 {
372            self as u16
373        }
374    }
375}
376
377/// Capabilities
378pub mod caps {
379    open_enum::open_enum! {
380        /// Capability IDs
381        ///
382        /// Sources: PCI 2.3 Spec - Appendix H
383        ///
384        /// NOTE: this is a non-exhaustive list, so don't be afraid to add new
385        /// variants on an as-needed basis!
386        pub enum CapabilityId: u8 {
387            #![expect(missing_docs)] // self explanatory variants
388            VENDOR_SPECIFIC = 0x09,
389            PCI_EXPRESS     = 0x10,
390            MSIX            = 0x11,
391        }
392    }
393
394    /// MSI-X
395    #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
396    pub mod msix {
397        open_enum::open_enum! {
398            /// Offsets into the MSI-X Capability Header
399            ///
400            /// Table pulled from <https://wiki.osdev.org/PCI>
401            ///
402            /// | Offset    | Bits 31-24         | Bits 23-16 | Bits 15-8    | Bits 7-3             | Bits 2-0 |
403            /// |-----------|--------------------|------------|--------------|----------------------|----------|
404            /// | Cap + 0x0 | Message Control    |            | Next Pointer | Capability ID (0x11) |          |
405            /// | Cap + 0x4 | Table Offset       |            |              |                      | BIR      |
406            /// | Cap + 0x8 | Pending Bit Offset |            |              |                      | BIR      |
407            pub enum MsixCapabilityHeader: u16 {
408                CONTROL_CAPS = 0x00,
409                OFFSET_TABLE = 0x04,
410                OFFSET_PBA   = 0x08,
411            }
412        }
413
414        open_enum::open_enum! {
415            /// Offsets into a single MSI-X Table Entry
416            pub enum MsixTableEntryIdx: u16 {
417                MSG_ADDR_LO = 0x00,
418                MSG_ADDR_HI = 0x04,
419                MSG_DATA    = 0x08,
420                VECTOR_CTL  = 0x0C,
421            }
422        }
423    }
424
425    /// PCI Express
426    #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
427    pub mod pci_express {
428        use bitfield_struct::bitfield;
429        use inspect::Inspect;
430        use zerocopy::FromBytes;
431        use zerocopy::Immutable;
432        use zerocopy::IntoBytes;
433        use zerocopy::KnownLayout;
434
435        open_enum::open_enum! {
436            /// Offsets into the PCI Express Capability Header
437            ///
438            /// Table pulled from PCI Express Base Specification Rev. 3.0
439            ///
440            /// | Offset    | Bits 31-24       | Bits 23-16       | Bits 15-8        | Bits 7-0             |
441            /// |-----------|------------------|----------------- |------------------|----------------------|
442            /// | Cap + 0x0 | PCI Express Capabilities Register   | Next Pointer     | Capability ID (0x10) |
443            /// | Cap + 0x4 | Device Capabilities Register                                                  |
444            /// | Cap + 0x8 | Device Status    | Device Control                                             |
445            pub enum PciExpressCapabilityHeader: u16 {
446                PCIE_CAPS       = 0x00,
447                DEVICE_CAPS     = 0x04,
448                DEVICE_CTL_STS  = 0x08,
449            }
450        }
451
452        /// PCI Express Capabilities Register
453        #[bitfield(u16)]
454        #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
455        pub struct PciExpressCapabilities {
456            #[bits(4)]
457            pub capability_version: u16,
458            #[bits(4)]
459            pub device_port_type: DevicePortType,
460            pub slot_implemented: bool,
461            #[bits(5)]
462            pub interrupt_message_number: u16,
463            pub _undefined: bool,
464            pub flit_mode_supported: bool,
465        }
466
467        #[derive(Debug)]
468        #[repr(u16)]
469        pub enum DevicePortType {
470            Endpoint = 0b0000,
471            RootPort = 0b0100,
472        }
473
474        impl DevicePortType {
475            const fn from_bits(bits: u16) -> Self {
476                match bits {
477                    0b0000 => DevicePortType::Endpoint,
478                    0b0100 => DevicePortType::RootPort,
479                    _ => unreachable!(),
480                }
481            }
482
483            const fn into_bits(self) -> u16 {
484                self as u16
485            }
486        }
487
488        /// Device Capabilities Register (From the 6.4 spec)
489        #[bitfield(u32)]
490        #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
491        pub struct DeviceCapabilities {
492            #[bits(3)]
493            pub max_payload_size: u32,
494            #[bits(2)]
495            pub phantom_functions: u32,
496            pub ext_tag_field: bool,
497            #[bits(3)]
498            pub endpoint_l0s_latency: u32,
499            #[bits(3)]
500            pub endpoint_l1_latency: u32,
501            #[bits(3)]
502            _reserved1: u32,
503            pub role_based_error: bool,
504            pub err_cor_subclass_capable: bool,
505            pub rx_mps_fixed: bool,
506            #[bits(8)]
507            pub captured_slot_power_limit: u32,
508            #[bits(2)]
509            pub captured_slot_power_scale: u32,
510            pub function_level_reset: bool,
511            pub mixed_mps_supported: bool,
512            pub tee_io_supported: bool,
513            _reserved3: bool,
514        }
515
516        /// Device Control Register
517        #[bitfield(u16)]
518        #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
519        pub struct DeviceControl {
520            pub correctable_error_reporting_enable: bool,
521            pub non_fatal_error_reporting_enable: bool,
522            pub fatal_error_reporting_enable: bool,
523            pub unsupported_request_reporting_enable: bool,
524            pub enable_relaxed_ordering: bool,
525            #[bits(3)]
526            pub max_payload_size: u16,
527            pub extended_tag_enable: bool,
528            pub phantom_functions_enable: bool,
529            pub aux_power_pm_enable: bool,
530            pub enable_no_snoop: bool,
531            #[bits(3)]
532            pub max_read_request_size: u16,
533            pub initiate_function_level_reset: bool,
534        }
535
536        /// Device Status Register
537        #[bitfield(u16)]
538        #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
539        pub struct DeviceStatus {
540            pub correctable_error_detected: bool,
541            pub non_fatal_error_detected: bool,
542            pub fatal_error_detected: bool,
543            pub unsupported_request_detected: bool,
544            pub aux_power_detected: bool,
545            pub transactions_pending: bool,
546            #[bits(10)]
547            _reserved: u16,
548        }
549    }
550}