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}