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_OTHER = 0x80,
131
132            // Base System Peripheral (Class code: 0x08)
133            // Other values: 0x00 - 0x06
134            BASE_SYSTEM_PERIPHERAL_OTHER = 0x80,
135        }
136    }
137
138    impl From<u8> for Subclass {
139        fn from(c: u8) -> Self {
140            Self(c)
141        }
142    }
143
144    impl From<Subclass> for u8 {
145        fn from(c: Subclass) -> Self {
146            c.0
147        }
148    }
149
150    open_enum::open_enum! {
151        /// ProgrammingInterface (aka, program interface byte) identifies the PCI device's
152        /// register-level programming interface.
153        ///
154        /// Values pulled from <https://wiki.osdev.org/PCI#Class_Codes>.
155        #[derive(Inspect)]
156        #[inspect(transparent(hex))]
157        pub enum ProgrammingInterface: u8{
158            // TODO: As more values are used, add them here.
159
160            NONE = 0x00,
161
162            // Non-Volatile Memory Controller (Class code:0x01, Subclass: 0x08)
163            // Other values: 0x01
164            MASS_STORAGE_CONTROLLER_NON_VOLATILE_MEMORY_NVME = 0x02,
165
166            // Ethernet Controller (Class code: 0x02, Subclass: 0x00)
167            NETWORK_CONTROLLER_ETHERNET_GDMA = 0x01,
168        }
169    }
170
171    impl From<u8> for ProgrammingInterface {
172        fn from(c: u8) -> Self {
173            Self(c)
174        }
175    }
176
177    impl From<ProgrammingInterface> for u8 {
178        fn from(c: ProgrammingInterface) -> Self {
179            c.0
180        }
181    }
182}
183
184/// Configuration Space
185///
186/// Sources: PCI 2.3 Spec - Chapter 6
187#[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
188pub mod cfg_space {
189    use bitfield_struct::bitfield;
190    use inspect::Inspect;
191    use zerocopy::FromBytes;
192    use zerocopy::Immutable;
193    use zerocopy::IntoBytes;
194    use zerocopy::KnownLayout;
195
196    open_enum::open_enum! {
197        /// Offsets into the type 00h configuration space header.
198        ///
199        /// Table pulled from <https://wiki.osdev.org/PCI>
200        ///
201        /// | Offset | Bits 31-24                 | Bits 23-16  | Bits 15-8           | Bits 7-0             |
202        /// |--------|----------------------------|-------------|---------------------|--------------------- |
203        /// | 0x0    | Device ID                  |             | Vendor ID           |                      |
204        /// | 0x4    | Status                     |             | Command             |                      |
205        /// | 0x8    | Class code                 |             |                     | Revision ID          |
206        /// | 0xC    | BIST                       | Header type | Latency Timer       | Cache Line Size      |
207        /// | 0x10   | Base address #0 (BAR0)     |             |                     |                      |
208        /// | 0x14   | Base address #1 (BAR1)     |             |                     |                      |
209        /// | 0x18   | Base address #2 (BAR2)     |             |                     |                      |
210        /// | 0x1C   | Base address #3 (BAR3)     |             |                     |                      |
211        /// | 0x20   | Base address #4 (BAR4)     |             |                     |                      |
212        /// | 0x24   | Base address #5 (BAR5)     |             |                     |                      |
213        /// | 0x28   | Cardbus CIS Pointer        |             |                     |                      |
214        /// | 0x2C   | Subsystem ID               |             | Subsystem Vendor ID |                      |
215        /// | 0x30   | Expansion ROM base address |             |                     |                      |
216        /// | 0x34   | Reserved                   |             |                     | Capabilities Pointer |
217        /// | 0x38   | Reserved                   |             |                     |                      |
218        /// | 0x3C   | Max latency                | Min Grant   | Interrupt PIN       | Interrupt Line       |
219        pub enum HeaderType00: u16 {
220            DEVICE_VENDOR      = 0x00,
221            STATUS_COMMAND     = 0x04,
222            CLASS_REVISION     = 0x08,
223            BIST_HEADER        = 0x0C,
224            BAR0               = 0x10,
225            BAR1               = 0x14,
226            BAR2               = 0x18,
227            BAR3               = 0x1C,
228            BAR4               = 0x20,
229            BAR5               = 0x24,
230            CARDBUS_CIS_PTR    = 0x28,
231            SUBSYSTEM_ID       = 0x2C,
232            EXPANSION_ROM_BASE = 0x30,
233            RESERVED_CAP_PTR   = 0x34,
234            RESERVED           = 0x38,
235            LATENCY_INTERRUPT  = 0x3C,
236        }
237    }
238
239    pub const HEADER_TYPE_00_SIZE: u16 = 0x40;
240
241    /// BAR in-band encoding bits.
242    ///
243    /// The low bits of the BAR are not actually part of the address.
244    /// Instead, they are used to in-band encode various bits of
245    /// metadata about the BAR, and are masked off when determining the
246    /// actual address.
247    #[bitfield(u32)]
248    pub struct BarEncodingBits {
249        pub use_pio: bool,
250
251        _reserved: bool,
252
253        /// False indicates 32 bit.
254        /// Only used in MMIO
255        pub type_64_bit: bool,
256        pub prefetchable: bool,
257
258        #[bits(28)]
259        _reserved2: u32,
260    }
261
262    /// Command Register
263    #[derive(Inspect)]
264    #[bitfield(u16)]
265    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
266    pub struct Command {
267        pub pio_enabled: bool,
268        pub mmio_enabled: bool,
269        pub bus_master: bool,
270        pub special_cycles: bool,
271        pub enable_memory_write_invalidate: bool,
272        pub vga_palette_snoop: bool,
273        pub parity_error_response: bool,
274        /// must be 0
275        #[bits(1)]
276        _reserved: u16,
277        pub enable_serr: bool,
278        pub enable_fast_b2b: bool,
279        pub intx_disable: bool,
280        #[bits(5)]
281        _reserved2: u16,
282    }
283
284    /// Status Register
285    #[bitfield(u16)]
286    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
287    pub struct Status {
288        #[bits(3)]
289        _reserved: u16,
290        pub interrupt_status: bool,
291        pub capabilities_list: bool,
292        pub capable_mhz_66: bool,
293        _reserved2: bool,
294        pub capable_fast_b2b: bool,
295        pub err_master_parity: bool,
296
297        #[bits(2)]
298        pub devsel: DevSel,
299
300        pub abort_target_signaled: bool,
301        pub abort_target_received: bool,
302        pub abort_master_received: bool,
303        pub err_signaled: bool,
304        pub err_detected_parity: bool,
305    }
306
307    #[derive(Debug)]
308    #[repr(u16)]
309    pub enum DevSel {
310        Fast = 0b00,
311        Medium = 0b01,
312        Slow = 0b10,
313    }
314
315    impl DevSel {
316        const fn from_bits(bits: u16) -> Self {
317            match bits {
318                0b00 => DevSel::Fast,
319                0b01 => DevSel::Medium,
320                0b10 => DevSel::Slow,
321                _ => unreachable!(),
322            }
323        }
324
325        const fn into_bits(self) -> u16 {
326            self as u16
327        }
328    }
329}
330
331/// Capabilities
332pub mod caps {
333    open_enum::open_enum! {
334        /// Capability IDs
335        ///
336        /// Sources: PCI 2.3 Spec - Appendix H
337        ///
338        /// NOTE: this is a non-exhaustive list, so don't be afraid to add new
339        /// variants on an as-needed basis!
340        pub enum CapabilityId: u8 {
341            #![expect(missing_docs)] // self explanatory variants
342            VENDOR_SPECIFIC = 0x09,
343            MSIX            = 0x11,
344        }
345    }
346
347    /// MSI-X
348    #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
349    pub mod msix {
350        open_enum::open_enum! {
351            /// Offsets into the MSI-X Capability Header
352            ///
353            /// Table pulled from <https://wiki.osdev.org/PCI>
354            ///
355            /// | Offset    | Bits 31-24         | Bits 23-16 | Bits 15-8    | Bits 7-3             | Bits 2-0 |
356            /// |-----------|--------------------|------------|--------------|----------------------|----------|
357            /// | Cap + 0x0 | Message Control    |            | Next Pointer | Capability ID (0x11) |          |
358            /// | Cap + 0x4 | Table Offset       |            |              |                      | BIR      |
359            /// | Cap + 0x8 | Pending Bit Offset |            |              |                      | BIR      |
360            pub enum MsixCapabilityHeader: u16 {
361                CONTROL_CAPS = 0x00,
362                OFFSET_TABLE = 0x04,
363                OFFSET_PBA   = 0x08,
364            }
365        }
366
367        open_enum::open_enum! {
368            /// Offsets into a single MSI-X Table Entry
369            pub enum MsixTableEntryIdx: u16 {
370                MSG_ADDR_LO = 0x00,
371                MSG_ADDR_HI = 0x04,
372                MSG_DATA    = 0x08,
373                VECTOR_CTL  = 0x0C,
374            }
375        }
376    }
377}