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}