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 = 0x00,
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 /// Common configuration space header registers shared between Type 0 and Type 1 headers.
199 ///
200 /// These registers appear at the same offsets in both header types and have the same
201 /// meaning and format.
202 ///
203 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-0 |
204 /// |--------|----------------|-------------|-------------|----------------------|
205 /// | 0x0 | Device ID | | Vendor ID | |
206 /// | 0x4 | Status | | Command | |
207 /// | 0x8 | Class code | | | Revision ID |
208 /// | 0x34 | Reserved | | | Capabilities Pointer |
209 pub enum CommonHeader: u16 {
210 DEVICE_VENDOR = 0x00,
211 STATUS_COMMAND = 0x04,
212 CLASS_REVISION = 0x08,
213 RESERVED_CAP_PTR = 0x34,
214 }
215 }
216
217 /// Size of the common header portion shared by all PCI header types.
218 pub const COMMON_HEADER_SIZE: u16 = 0x10;
219
220 open_enum::open_enum! {
221 /// Offsets into the type 00h configuration space header.
222 ///
223 /// Table pulled from <https://wiki.osdev.org/PCI>
224 ///
225 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-0 |
226 /// |--------|----------------------------|-------------|---------------------|--------------------- |
227 /// | 0x0 | Device ID | | Vendor ID | |
228 /// | 0x4 | Status | | Command | |
229 /// | 0x8 | Class code | | | Revision ID |
230 /// | 0xC | BIST | Header type | Latency Timer | Cache Line Size |
231 /// | 0x10 | Base address #0 (BAR0) | | | |
232 /// | 0x14 | Base address #1 (BAR1) | | | |
233 /// | 0x18 | Base address #2 (BAR2) | | | |
234 /// | 0x1C | Base address #3 (BAR3) | | | |
235 /// | 0x20 | Base address #4 (BAR4) | | | |
236 /// | 0x24 | Base address #5 (BAR5) | | | |
237 /// | 0x28 | Cardbus CIS Pointer | | | |
238 /// | 0x2C | Subsystem ID | | Subsystem Vendor ID | |
239 /// | 0x30 | Expansion ROM base address | | | |
240 /// | 0x34 | Reserved | | | Capabilities Pointer |
241 /// | 0x38 | Reserved | | | |
242 /// | 0x3C | Max latency | Min Grant | Interrupt PIN | Interrupt Line |
243 pub enum HeaderType00: u16 {
244 DEVICE_VENDOR = 0x00,
245 STATUS_COMMAND = 0x04,
246 CLASS_REVISION = 0x08,
247 BIST_HEADER = 0x0C,
248 BAR0 = 0x10,
249 BAR1 = 0x14,
250 BAR2 = 0x18,
251 BAR3 = 0x1C,
252 BAR4 = 0x20,
253 BAR5 = 0x24,
254 CARDBUS_CIS_PTR = 0x28,
255 SUBSYSTEM_ID = 0x2C,
256 EXPANSION_ROM_BASE = 0x30,
257 RESERVED_CAP_PTR = 0x34,
258 RESERVED = 0x38,
259 LATENCY_INTERRUPT = 0x3C,
260 }
261 }
262
263 pub const HEADER_TYPE_00_SIZE: u16 = 0x40;
264
265 open_enum::open_enum! {
266 /// Offsets into the type 01h configuration space header.
267 ///
268 /// Table pulled from <https://wiki.osdev.org/PCI>
269 ///
270 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-0 |
271 /// |--------|----------------------------------|------------------------|--------------------------|--------------------- |
272 /// | 0x0 | Device ID | | Vendor ID | |
273 /// | 0x4 | Status | | Command | |
274 /// | 0x8 | Class code | | | Revision ID |
275 /// | 0xC | BIST | Header Type | Latency Timer | Cache Line Size |
276 /// | 0x10 | Base address #0 (BAR0) | | | |
277 /// | 0x14 | Base address #1 (BAR1) | | | |
278 /// | 0x18 | Secondary Latency Timer | Subordinate Bus Number | Secondary Bus Number | Primary Bus Number |
279 /// | 0x1C | Secondary Status | | I/O Limit | I/O Base |
280 /// | 0x20 | Memory Limit | | Memory Base | |
281 /// | 0x24 | Prefetchable Memory Limit | | Prefetchable Memory Base | |
282 /// | 0x28 | Prefetchable Base Upper 32 Bits | | | |
283 /// | 0x2C | Prefetchable Limit Upper 32 Bits | | | |
284 /// | 0x30 | I/O Limit Upper 16 Bits | | I/O Base Upper 16 Bits | |
285 /// | 0x34 | Reserved | | | Capabilities Pointer |
286 /// | 0x38 | Expansion ROM Base Address | | | |
287 /// | 0x3C | Bridge Control | | Interrupt PIN | Interrupt Line |
288 pub enum HeaderType01: u16 {
289 DEVICE_VENDOR = 0x00,
290 STATUS_COMMAND = 0x04,
291 CLASS_REVISION = 0x08,
292 BIST_HEADER = 0x0C,
293 BAR0 = 0x10,
294 BAR1 = 0x14,
295 LATENCY_BUS_NUMBERS = 0x18,
296 SEC_STATUS_IO_RANGE = 0x1C,
297 MEMORY_RANGE = 0x20,
298 PREFETCH_RANGE = 0x24,
299 PREFETCH_BASE_UPPER = 0x28,
300 PREFETCH_LIMIT_UPPER = 0x2C,
301 IO_RANGE_UPPER = 0x30,
302 RESERVED_CAP_PTR = 0x34,
303 EXPANSION_ROM_BASE = 0x38,
304 BRDIGE_CTRL_INTERRUPT = 0x3C,
305 }
306 }
307
308 pub const HEADER_TYPE_01_SIZE: u16 = 0x40;
309
310 /// BAR in-band encoding bits.
311 ///
312 /// The low bits of the BAR are not actually part of the address.
313 /// Instead, they are used to in-band encode various bits of
314 /// metadata about the BAR, and are masked off when determining the
315 /// actual address.
316 #[bitfield(u32)]
317 pub struct BarEncodingBits {
318 pub use_pio: bool,
319
320 _reserved: bool,
321
322 /// False indicates 32 bit.
323 /// Only used in MMIO
324 pub type_64_bit: bool,
325 pub prefetchable: bool,
326
327 #[bits(28)]
328 _reserved2: u32,
329 }
330
331 /// Command Register
332 #[derive(Inspect)]
333 #[bitfield(u16)]
334 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
335 pub struct Command {
336 pub pio_enabled: bool,
337 pub mmio_enabled: bool,
338 pub bus_master: bool,
339 pub special_cycles: bool,
340 pub enable_memory_write_invalidate: bool,
341 pub vga_palette_snoop: bool,
342 pub parity_error_response: bool,
343 /// must be 0
344 #[bits(1)]
345 _reserved: u16,
346 pub enable_serr: bool,
347 pub enable_fast_b2b: bool,
348 pub intx_disable: bool,
349 #[bits(5)]
350 _reserved2: u16,
351 }
352
353 /// Status Register
354 #[bitfield(u16)]
355 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
356 pub struct Status {
357 #[bits(3)]
358 _reserved: u16,
359 pub interrupt_status: bool,
360 pub capabilities_list: bool,
361 pub capable_mhz_66: bool,
362 _reserved2: bool,
363 pub capable_fast_b2b: bool,
364 pub err_master_parity: bool,
365
366 #[bits(2)]
367 pub devsel: DevSel,
368
369 pub abort_target_signaled: bool,
370 pub abort_target_received: bool,
371 pub abort_master_received: bool,
372 pub err_signaled: bool,
373 pub err_detected_parity: bool,
374 }
375
376 #[derive(Debug)]
377 #[repr(u16)]
378 pub enum DevSel {
379 Fast = 0b00,
380 Medium = 0b01,
381 Slow = 0b10,
382 }
383
384 impl DevSel {
385 const fn from_bits(bits: u16) -> Self {
386 match bits {
387 0b00 => DevSel::Fast,
388 0b01 => DevSel::Medium,
389 0b10 => DevSel::Slow,
390 _ => unreachable!(),
391 }
392 }
393
394 const fn into_bits(self) -> u16 {
395 self as u16
396 }
397 }
398}
399
400/// Capabilities
401pub mod caps {
402 open_enum::open_enum! {
403 /// Capability IDs
404 ///
405 /// Sources: PCI 2.3 Spec - Appendix H
406 ///
407 /// NOTE: this is a non-exhaustive list, so don't be afraid to add new
408 /// variants on an as-needed basis!
409 pub enum CapabilityId: u8 {
410 #![expect(missing_docs)] // self explanatory variants
411 MSI = 0x05,
412 VENDOR_SPECIFIC = 0x09,
413 PCI_EXPRESS = 0x10,
414 MSIX = 0x11,
415 }
416 }
417
418 /// MSI
419 #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
420 pub mod msi {
421 open_enum::open_enum! {
422 /// Offsets into the MSI Capability Header
423 ///
424 /// Based on PCI Local Bus Specification Rev 3.0, Section 6.8.1
425 ///
426 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-0 |
427 /// |-----------|---------------|---------------|---------------|-----------------------|
428 /// | Cap + 0x0 | Message Control | Next Pointer | Capability ID (0x05) |
429 /// | Cap + 0x4 | Message Address (32-bit or lower 32-bit of 64-bit) |
430 /// | Cap + 0x8 | Message Address Upper 32-bit (64-bit capable only) |
431 /// | Cap + 0xC | Message Data | | | |
432 /// | Cap + 0x10| Mask Bits (Per-vector masking capable only) |
433 /// | Cap + 0x14| Pending Bits (Per-vector masking capable only) |
434 pub enum MsiCapabilityHeader: u16 {
435 CONTROL_CAPS = 0x00,
436 MSG_ADDR_LO = 0x04,
437 MSG_ADDR_HI = 0x08,
438 MSG_DATA_32 = 0x08, // For 32-bit address capable
439 MSG_DATA_64 = 0x0C, // For 64-bit address capable
440 MASK_BITS = 0x10, // 64-bit + per-vector masking
441 PENDING_BITS = 0x14, // 64-bit + per-vector masking
442 }
443 }
444 }
445
446 /// MSI-X
447 #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
448 pub mod msix {
449 open_enum::open_enum! {
450 /// Offsets into the MSI-X Capability Header
451 ///
452 /// Table pulled from <https://wiki.osdev.org/PCI>
453 ///
454 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-3 | Bits 2-0 |
455 /// |-----------|--------------------|------------|--------------|----------------------|----------|
456 /// | Cap + 0x0 | Message Control | | Next Pointer | Capability ID (0x11) | |
457 /// | Cap + 0x4 | Table Offset | | | | BIR |
458 /// | Cap + 0x8 | Pending Bit Offset | | | | BIR |
459 pub enum MsixCapabilityHeader: u16 {
460 CONTROL_CAPS = 0x00,
461 OFFSET_TABLE = 0x04,
462 OFFSET_PBA = 0x08,
463 }
464 }
465
466 open_enum::open_enum! {
467 /// Offsets into a single MSI-X Table Entry
468 pub enum MsixTableEntryIdx: u16 {
469 MSG_ADDR_LO = 0x00,
470 MSG_ADDR_HI = 0x04,
471 MSG_DATA = 0x08,
472 VECTOR_CTL = 0x0C,
473 }
474 }
475 }
476
477 /// PCI Express
478 #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
479 pub mod pci_express {
480 use bitfield_struct::bitfield;
481 use inspect::Inspect;
482 use zerocopy::FromBytes;
483 use zerocopy::Immutable;
484 use zerocopy::IntoBytes;
485 use zerocopy::KnownLayout;
486
487 /// PCIe Link Speed encoding values for use in Link Capabilities and other registers.
488 ///
489 /// Values are defined in PCIe Base Specification for the Max Link Speed field
490 /// in Link Capabilities Register and similar fields.
491 #[derive(Debug)]
492 #[repr(u32)]
493 pub enum LinkSpeed {
494 /// 2.5 GT/s link speed
495 Speed2_5GtS = 0b0001,
496 /// 5.0 GT/s link speed
497 Speed5_0GtS = 0b0010,
498 /// 8.0 GT/s link speed
499 Speed8_0GtS = 0b0011,
500 /// 16.0 GT/s link speed
501 Speed16_0GtS = 0b0100,
502 /// 32.0 GT/s link speed
503 Speed32_0GtS = 0b0101,
504 /// 64.0 GT/s link speed
505 Speed64_0GtS = 0b0110,
506 // All other encodings are reserved
507 }
508
509 impl LinkSpeed {
510 pub const fn from_bits(bits: u32) -> Self {
511 match bits {
512 0b0001 => LinkSpeed::Speed2_5GtS,
513 0b0010 => LinkSpeed::Speed5_0GtS,
514 0b0011 => LinkSpeed::Speed8_0GtS,
515 0b0100 => LinkSpeed::Speed16_0GtS,
516 0b0101 => LinkSpeed::Speed32_0GtS,
517 0b0110 => LinkSpeed::Speed64_0GtS,
518 _ => unreachable!(),
519 }
520 }
521
522 pub const fn into_bits(self) -> u32 {
523 self as u32
524 }
525 }
526
527 /// PCIe Supported Link Speeds Vector encoding values for use in Link Capabilities 2 register.
528 ///
529 /// Values are defined in PCIe Base Specification for the Supported Link Speeds Vector field
530 /// in Link Capabilities 2 Register. Each bit represents support for a specific generation.
531 #[derive(Debug)]
532 #[repr(u32)]
533 pub enum SupportedLinkSpeedsVector {
534 /// Support up to Gen 1 (2.5 GT/s)
535 UpToGen1 = 0b0000001,
536 /// Support up to Gen 2 (5.0 GT/s)
537 UpToGen2 = 0b0000011,
538 /// Support up to Gen 3 (8.0 GT/s)
539 UpToGen3 = 0b0000111,
540 /// Support up to Gen 4 (16.0 GT/s)
541 UpToGen4 = 0b0001111,
542 /// Support up to Gen 5 (32.0 GT/s)
543 UpToGen5 = 0b0011111,
544 /// Support up to Gen 6 (64.0 GT/s)
545 UpToGen6 = 0b0111111,
546 // All other encodings are reserved
547 }
548
549 impl SupportedLinkSpeedsVector {
550 pub const fn from_bits(bits: u32) -> Self {
551 match bits {
552 0b0000001 => SupportedLinkSpeedsVector::UpToGen1,
553 0b0000011 => SupportedLinkSpeedsVector::UpToGen2,
554 0b0000111 => SupportedLinkSpeedsVector::UpToGen3,
555 0b0001111 => SupportedLinkSpeedsVector::UpToGen4,
556 0b0011111 => SupportedLinkSpeedsVector::UpToGen5,
557 0b0111111 => SupportedLinkSpeedsVector::UpToGen6,
558 _ => unreachable!(),
559 }
560 }
561
562 pub const fn into_bits(self) -> u32 {
563 self as u32
564 }
565 }
566
567 /// PCIe Link Width encoding values for use in Link Capabilities and other registers.
568 ///
569 /// Values are defined in PCIe Base Specification for the Max Link Width field
570 /// in Link Capabilities Register and similar fields.
571 #[derive(Debug)]
572 #[repr(u32)]
573 pub enum LinkWidth {
574 /// x1 link width
575 X1 = 0b000001,
576 /// x2 link width
577 X2 = 0b000010,
578 /// x4 link width
579 X4 = 0b000100,
580 /// x8 link width
581 X8 = 0b001000,
582 /// x16 link width
583 X16 = 0b010000,
584 // All other encodings are reserved
585 }
586
587 impl LinkWidth {
588 pub const fn from_bits(bits: u32) -> Self {
589 match bits {
590 0b000001 => LinkWidth::X1,
591 0b000010 => LinkWidth::X2,
592 0b000100 => LinkWidth::X4,
593 0b001000 => LinkWidth::X8,
594 0b010000 => LinkWidth::X16,
595 _ => unreachable!(),
596 }
597 }
598
599 pub const fn into_bits(self) -> u32 {
600 self as u32
601 }
602 }
603
604 open_enum::open_enum! {
605 /// Offsets into the PCI Express Capability Header
606 ///
607 /// Table pulled from PCI Express Base Specification Rev. 3.0
608 ///
609 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-0 |
610 /// |-----------|------------------|----------------- |------------------|----------------------|
611 /// | Cap + 0x0 | PCI Express Capabilities Register | Next Pointer | Capability ID (0x10) |
612 /// | Cap + 0x4 | Device Capabilities Register |
613 /// | Cap + 0x8 | Device Status | Device Control |
614 /// | Cap + 0xC | Link Capabilities Register |
615 /// | Cap + 0x10| Link Status | Link Control |
616 /// | Cap + 0x14| Slot Capabilities Register |
617 /// | Cap + 0x18| Slot Status | Slot Control |
618 /// | Cap + 0x1C| Root Capabilities| Root Control |
619 /// | Cap + 0x20| Root Status Register |
620 /// | Cap + 0x24| Device Capabilities 2 Register |
621 /// | Cap + 0x28| Device Status 2 | Device Control 2 |
622 /// | Cap + 0x2C| Link Capabilities 2 Register |
623 /// | Cap + 0x30| Link Status 2 | Link Control 2 |
624 /// | Cap + 0x34| Slot Capabilities 2 Register |
625 /// | Cap + 0x38| Slot Status 2 | Slot Control 2 |
626 pub enum PciExpressCapabilityHeader: u16 {
627 PCIE_CAPS = 0x00,
628 DEVICE_CAPS = 0x04,
629 DEVICE_CTL_STS = 0x08,
630 LINK_CAPS = 0x0C,
631 LINK_CTL_STS = 0x10,
632 SLOT_CAPS = 0x14,
633 SLOT_CTL_STS = 0x18,
634 ROOT_CTL_CAPS = 0x1C,
635 ROOT_STS = 0x20,
636 DEVICE_CAPS_2 = 0x24,
637 DEVICE_CTL_STS_2 = 0x28,
638 LINK_CAPS_2 = 0x2C,
639 LINK_CTL_STS_2 = 0x30,
640 SLOT_CAPS_2 = 0x34,
641 SLOT_CTL_STS_2 = 0x38,
642 }
643 }
644
645 /// PCI Express Capabilities Register
646 #[bitfield(u16)]
647 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
648 pub struct PciExpressCapabilities {
649 #[bits(4)]
650 pub capability_version: u16,
651 #[bits(4)]
652 pub device_port_type: DevicePortType,
653 pub slot_implemented: bool,
654 #[bits(5)]
655 pub interrupt_message_number: u16,
656 pub _undefined: bool,
657 pub flit_mode_supported: bool,
658 }
659
660 #[derive(Debug)]
661 #[repr(u16)]
662 pub enum DevicePortType {
663 Endpoint = 0b0000,
664 RootPort = 0b0100,
665 UpstreamSwitchPort = 0b0101,
666 DownstreamSwitchPort = 0b0110,
667 }
668
669 impl DevicePortType {
670 const fn from_bits(bits: u16) -> Self {
671 match bits {
672 0b0000 => DevicePortType::Endpoint,
673 0b0100 => DevicePortType::RootPort,
674 0b0101 => DevicePortType::UpstreamSwitchPort,
675 0b0110 => DevicePortType::DownstreamSwitchPort,
676 _ => unreachable!(),
677 }
678 }
679
680 const fn into_bits(self) -> u16 {
681 self as u16
682 }
683 }
684
685 /// Device Capabilities Register (From the 6.4 spec)
686 #[bitfield(u32)]
687 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
688 pub struct DeviceCapabilities {
689 #[bits(3)]
690 pub max_payload_size: u32,
691 #[bits(2)]
692 pub phantom_functions: u32,
693 pub ext_tag_field: bool,
694 #[bits(3)]
695 pub endpoint_l0s_latency: u32,
696 #[bits(3)]
697 pub endpoint_l1_latency: u32,
698 #[bits(3)]
699 _reserved1: u32,
700 pub role_based_error: bool,
701 pub err_cor_subclass_capable: bool,
702 pub rx_mps_fixed: bool,
703 #[bits(8)]
704 pub captured_slot_power_limit: u32,
705 #[bits(2)]
706 pub captured_slot_power_scale: u32,
707 pub function_level_reset: bool,
708 pub mixed_mps_supported: bool,
709 pub tee_io_supported: bool,
710 _reserved3: bool,
711 }
712
713 /// Device Control Register
714 #[bitfield(u16)]
715 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
716 pub struct DeviceControl {
717 pub correctable_error_reporting_enable: bool,
718 pub non_fatal_error_reporting_enable: bool,
719 pub fatal_error_reporting_enable: bool,
720 pub unsupported_request_reporting_enable: bool,
721 pub enable_relaxed_ordering: bool,
722 #[bits(3)]
723 pub max_payload_size: u16,
724 pub extended_tag_enable: bool,
725 pub phantom_functions_enable: bool,
726 pub aux_power_pm_enable: bool,
727 pub enable_no_snoop: bool,
728 #[bits(3)]
729 pub max_read_request_size: u16,
730 pub initiate_function_level_reset: bool,
731 }
732
733 /// Device Status Register
734 #[bitfield(u16)]
735 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
736 pub struct DeviceStatus {
737 pub correctable_error_detected: bool,
738 pub non_fatal_error_detected: bool,
739 pub fatal_error_detected: bool,
740 pub unsupported_request_detected: bool,
741 pub aux_power_detected: bool,
742 pub transactions_pending: bool,
743 #[bits(10)]
744 _reserved: u16,
745 }
746
747 /// Link Capabilities Register
748 #[bitfield(u32)]
749 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
750 pub struct LinkCapabilities {
751 #[bits(4)]
752 pub max_link_speed: u32,
753 #[bits(6)]
754 pub max_link_width: u32,
755 #[bits(2)]
756 pub aspm_support: u32,
757 #[bits(3)]
758 pub l0s_exit_latency: u32,
759 #[bits(3)]
760 pub l1_exit_latency: u32,
761 pub clock_power_management: bool,
762 pub surprise_down_error_reporting: bool,
763 pub data_link_layer_link_active_reporting: bool,
764 pub link_bandwidth_notification_capability: bool,
765 pub aspm_optionality_compliance: bool,
766 #[bits(1)]
767 _reserved: u32,
768 #[bits(8)]
769 pub port_number: u32,
770 }
771
772 /// Link Control Register
773 #[bitfield(u16)]
774 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
775 pub struct LinkControl {
776 #[bits(2)]
777 pub aspm_control: u16,
778 pub ptm_propagation_delay_adaptation_interpretation_b: bool,
779 #[bits(1)]
780 pub read_completion_boundary: u16,
781 pub link_disable: bool,
782 pub retrain_link: bool,
783 pub common_clock_configuration: bool,
784 pub extended_synch: bool,
785 pub enable_clock_power_management: bool,
786 pub hardware_autonomous_width_disable: bool,
787 pub link_bandwidth_management_interrupt_enable: bool,
788 pub link_autonomous_bandwidth_interrupt_enable: bool,
789 #[bits(1)]
790 pub sris_clocking: u16,
791 pub flit_mode_disable: bool,
792 #[bits(2)]
793 pub drs_signaling_control: u16,
794 }
795
796 /// Link Status Register
797 #[bitfield(u16)]
798 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
799 pub struct LinkStatus {
800 #[bits(4)]
801 pub current_link_speed: u16,
802 #[bits(6)]
803 pub negotiated_link_width: u16,
804 #[bits(1)]
805 _reserved: u16,
806 pub link_training: bool,
807 pub slot_clock_configuration: bool,
808 pub data_link_layer_link_active: bool,
809 pub link_bandwidth_management_status: bool,
810 pub link_autonomous_bandwidth_status: bool,
811 }
812
813 /// Slot Capabilities Register
814 #[bitfield(u32)]
815 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
816 pub struct SlotCapabilities {
817 pub attention_button_present: bool,
818 pub power_controller_present: bool,
819 pub mrl_sensor_present: bool,
820 pub attention_indicator_present: bool,
821 pub power_indicator_present: bool,
822 pub hot_plug_surprise: bool,
823 pub hot_plug_capable: bool,
824 #[bits(8)]
825 pub slot_power_limit_value: u32,
826 #[bits(2)]
827 pub slot_power_limit_scale: u32,
828 pub electromechanical_interlock_present: bool,
829 pub no_command_completed_support: bool,
830 #[bits(13)]
831 pub physical_slot_number: u32,
832 }
833
834 /// Slot Control Register
835 #[bitfield(u16)]
836 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
837 pub struct SlotControl {
838 pub attention_button_pressed_enable: bool,
839 pub power_fault_detected_enable: bool,
840 pub mrl_sensor_changed_enable: bool,
841 pub presence_detect_changed_enable: bool,
842 pub command_completed_interrupt_enable: bool,
843 pub hot_plug_interrupt_enable: bool,
844 #[bits(2)]
845 pub attention_indicator_control: u16,
846 #[bits(2)]
847 pub power_indicator_control: u16,
848 pub power_controller_control: bool,
849 pub electromechanical_interlock_control: bool,
850 pub data_link_layer_state_changed_enable: bool,
851 pub auto_slot_power_limit_enable: bool,
852 pub in_band_pd_disable: bool,
853 #[bits(1)]
854 _reserved: u16,
855 }
856
857 /// Slot Status Register
858 #[bitfield(u16)]
859 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
860 pub struct SlotStatus {
861 pub attention_button_pressed: bool,
862 pub power_fault_detected: bool,
863 pub mrl_sensor_changed: bool,
864 pub presence_detect_changed: bool,
865 pub command_completed: bool,
866 #[bits(1)]
867 pub mrl_sensor_state: u16,
868 #[bits(1)]
869 pub presence_detect_state: u16,
870 #[bits(1)]
871 pub electromechanical_interlock_status: u16,
872 pub data_link_layer_state_changed: bool,
873 #[bits(7)]
874 _reserved: u16,
875 }
876
877 /// Root Control Register
878 #[bitfield(u16)]
879 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
880 pub struct RootControl {
881 pub system_error_on_correctable_error_enable: bool,
882 pub system_error_on_non_fatal_error_enable: bool,
883 pub system_error_on_fatal_error_enable: bool,
884 pub pme_interrupt_enable: bool,
885 pub crs_software_visibility_enable: bool,
886 pub no_nfm_subtree_below_this_root_port: bool,
887 #[bits(10)]
888 _reserved: u16,
889 }
890
891 /// Root Capabilities Register
892 #[bitfield(u16)]
893 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
894 pub struct RootCapabilities {
895 pub crs_software_visibility: bool,
896 #[bits(15)]
897 _reserved: u16,
898 }
899
900 /// Root Status Register
901 #[bitfield(u32)]
902 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
903 pub struct RootStatus {
904 #[bits(16)]
905 pub pme_requester_id: u32,
906 pub pme_status: bool,
907 pub pme_pending: bool,
908 #[bits(14)]
909 _reserved: u32,
910 }
911
912 /// Device Capabilities 2 Register
913 #[bitfield(u32)]
914 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
915 pub struct DeviceCapabilities2 {
916 #[bits(4)]
917 pub completion_timeout_ranges_supported: u32,
918 pub completion_timeout_disable_supported: bool,
919 pub ari_forwarding_supported: bool,
920 pub atomic_op_routing_supported: bool,
921 pub atomic_op_32_bit_completer_supported: bool,
922 pub atomic_op_64_bit_completer_supported: bool,
923 pub cas_128_bit_completer_supported: bool,
924 pub no_ro_enabled_pr_pr_passing: bool,
925 pub ltr_mechanism_supported: bool,
926 #[bits(2)]
927 pub tph_completer_supported: u32,
928 #[bits(2)]
929 _reserved: u32,
930 pub ten_bit_tag_completer_supported: bool,
931 pub ten_bit_tag_requester_supported: bool,
932 #[bits(2)]
933 pub obff_supported: u32,
934 pub extended_fmt_field_supported: bool,
935 pub end_end_tlp_prefix_supported: bool,
936 #[bits(2)]
937 pub max_end_end_tlp_prefixes: u32,
938 #[bits(2)]
939 pub emergency_power_reduction_supported: u32,
940 pub emergency_power_reduction_init_required: bool,
941 #[bits(1)]
942 _reserved: u32,
943 pub dmwr_completer_supported: bool,
944 #[bits(2)]
945 pub dmwr_lengths_supported: u32,
946 pub frs_supported: bool,
947 }
948
949 /// Device Control 2 Register
950 #[bitfield(u16)]
951 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
952 pub struct DeviceControl2 {
953 #[bits(4)]
954 pub completion_timeout_value: u16,
955 pub completion_timeout_disable: bool,
956 pub ari_forwarding_enable: bool,
957 pub atomic_op_requester_enable: bool,
958 pub atomic_op_egress_blocking: bool,
959 pub ido_request_enable: bool,
960 pub ido_completion_enable: bool,
961 pub ltr_mechanism_enable: bool,
962 pub emergency_power_reduction_request: bool,
963 pub ten_bit_tag_requester_enable: bool,
964 #[bits(2)]
965 pub obff_enable: u16,
966 pub end_end_tlp_prefix_blocking: bool,
967 }
968
969 /// Device Status 2 Register
970 #[bitfield(u16)]
971 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
972 pub struct DeviceStatus2 {
973 #[bits(16)]
974 _reserved: u16,
975 }
976
977 /// Link Capabilities 2 Register
978 #[bitfield(u32)]
979 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
980 pub struct LinkCapabilities2 {
981 #[bits(1)]
982 _reserved: u32,
983 #[bits(7)]
984 pub supported_link_speeds_vector: u32,
985 pub crosslink_supported: bool,
986 #[bits(7)]
987 pub lower_skp_os_generation_supported_speeds_vector: u32,
988 #[bits(7)]
989 pub lower_skp_os_reception_supported_speeds_vector: u32,
990 pub retimer_presence_detect_supported: bool,
991 pub two_retimers_presence_detect_supported: bool,
992 #[bits(6)]
993 _reserved: u32,
994 pub drs_supported: bool,
995 }
996
997 /// Link Control 2 Register
998 #[bitfield(u16)]
999 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1000 pub struct LinkControl2 {
1001 #[bits(4)]
1002 pub target_link_speed: u16,
1003 pub enter_compliance: bool,
1004 pub hardware_autonomous_speed_disable: bool,
1005 #[bits(1)]
1006 pub selectable_de_emphasis: u16,
1007 #[bits(3)]
1008 pub transmit_margin: u16,
1009 pub enter_modified_compliance: bool,
1010 pub compliance_sos: bool,
1011 #[bits(4)]
1012 pub compliance_preset_de_emphasis: u16,
1013 }
1014
1015 /// Link Status 2 Register
1016 #[bitfield(u16)]
1017 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1018 pub struct LinkStatus2 {
1019 #[bits(1)]
1020 pub current_de_emphasis_level: u16,
1021 pub equalization_8gts_complete: bool,
1022 pub equalization_8gts_phase_1_successful: bool,
1023 pub equalization_8gts_phase_2_successful: bool,
1024 pub equalization_8gts_phase_3_successful: bool,
1025 pub link_equalization_request_8gts: bool,
1026 pub retimer_presence_detected: bool,
1027 pub two_retimers_presence_detected: bool,
1028 #[bits(2)]
1029 pub crosslink_resolution: u16,
1030 pub flit_mode_status: bool,
1031 #[bits(1)]
1032 _reserved: u16,
1033 #[bits(3)]
1034 pub downstream_component_presence: u16,
1035 pub drs_message_received: bool,
1036 }
1037
1038 /// Slot Capabilities 2 Register
1039 #[bitfield(u32)]
1040 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1041 pub struct SlotCapabilities2 {
1042 pub in_band_pd_disable_supported: bool,
1043 #[bits(31)]
1044 _reserved: u32,
1045 }
1046
1047 /// Slot Control 2 Register
1048 #[bitfield(u16)]
1049 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1050 pub struct SlotControl2 {
1051 #[bits(16)]
1052 _reserved: u16,
1053 }
1054
1055 /// Slot Status 2 Register
1056 #[bitfield(u16)]
1057 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1058 pub struct SlotStatus2 {
1059 #[bits(16)]
1060 _reserved: u16,
1061 }
1062 }
1063}