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_SCSI = 0x00,
121 MASS_STORAGE_CONTROLLER_NON_VOLATILE_MEMORY = 0x08,
122
123 // Network Controller (Class code: 0x02)
124 // Other values: 0x01 - 0x08, 0x80
125 NETWORK_CONTROLLER_ETHERNET = 0x00,
126
127 // Simple Communication Controller (Class code: 0x07)
128 // Other values: 0x00 - 0x07
129 SIMPLE_COMMUNICATION_CONTROLLER_OTHER = 0x80,
130
131 // Bridge (Class code: 0x06)
132 // Other values: 0x02 - 0x0A
133 BRIDGE_HOST = 0x00,
134 BRIDGE_ISA = 0x01,
135 BRIDGE_PCI_TO_PCI = 0x04,
136 BRIDGE_OTHER = 0x80,
137
138 // Base System Peripheral (Class code: 0x08)
139 // Other values: 0x00 - 0x06
140 BASE_SYSTEM_PERIPHERAL_OTHER = 0x80,
141 }
142 }
143
144 impl From<u8> for Subclass {
145 fn from(c: u8) -> Self {
146 Self(c)
147 }
148 }
149
150 impl From<Subclass> for u8 {
151 fn from(c: Subclass) -> Self {
152 c.0
153 }
154 }
155
156 open_enum::open_enum! {
157 /// ProgrammingInterface (aka, program interface byte) identifies the PCI device's
158 /// register-level programming interface.
159 ///
160 /// Values pulled from <https://wiki.osdev.org/PCI#Class_Codes>.
161 #[derive(Inspect)]
162 #[inspect(transparent(hex))]
163 pub enum ProgrammingInterface: u8{
164 // TODO: As more values are used, add them here.
165
166 NONE = 0x00,
167
168 // Non-Volatile Memory Controller (Class code:0x01, Subclass: 0x08)
169 // Other values: 0x01
170 MASS_STORAGE_CONTROLLER_NON_VOLATILE_MEMORY_NVME = 0x02,
171
172 // Ethernet Controller (Class code: 0x02, Subclass: 0x00)
173 NETWORK_CONTROLLER_ETHERNET_GDMA = 0x00,
174 }
175 }
176
177 impl From<u8> for ProgrammingInterface {
178 fn from(c: u8) -> Self {
179 Self(c)
180 }
181 }
182
183 impl From<ProgrammingInterface> for u8 {
184 fn from(c: ProgrammingInterface) -> Self {
185 c.0
186 }
187 }
188}
189
190/// Configuration Space
191///
192/// Sources: PCI 2.3 Spec - Chapter 6
193#[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
194pub mod cfg_space {
195 use bitfield_struct::bitfield;
196 use inspect::Inspect;
197 use zerocopy::FromBytes;
198 use zerocopy::Immutable;
199 use zerocopy::IntoBytes;
200 use zerocopy::KnownLayout;
201
202 open_enum::open_enum! {
203 /// Common configuration space header registers shared between Type 0 and Type 1 headers.
204 ///
205 /// These registers appear at the same offsets in both header types and have the same
206 /// meaning and format.
207 ///
208 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-0 |
209 /// |--------|----------------|-------------|-------------|----------------------|
210 /// | 0x0 | Device ID | | Vendor ID | |
211 /// | 0x4 | Status | | Command | |
212 /// | 0x8 | Class code | | | Revision ID |
213 /// | 0x34 | Reserved | | | Capabilities Pointer |
214 pub enum CommonHeader: u16 {
215 DEVICE_VENDOR = 0x00,
216 STATUS_COMMAND = 0x04,
217 CLASS_REVISION = 0x08,
218 RESERVED_CAP_PTR = 0x34,
219 }
220 }
221
222 /// Size of the common header portion shared by all PCI header types.
223 pub const COMMON_HEADER_SIZE: u16 = 0x10;
224
225 open_enum::open_enum! {
226 /// Offsets into the type 00h configuration space header.
227 ///
228 /// Table pulled from <https://wiki.osdev.org/PCI>
229 ///
230 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-0 |
231 /// |--------|----------------------------|-------------|---------------------|--------------------- |
232 /// | 0x0 | Device ID | | Vendor ID | |
233 /// | 0x4 | Status | | Command | |
234 /// | 0x8 | Class code | | | Revision ID |
235 /// | 0xC | BIST | Header type | Latency Timer | Cache Line Size |
236 /// | 0x10 | Base address #0 (BAR0) | | | |
237 /// | 0x14 | Base address #1 (BAR1) | | | |
238 /// | 0x18 | Base address #2 (BAR2) | | | |
239 /// | 0x1C | Base address #3 (BAR3) | | | |
240 /// | 0x20 | Base address #4 (BAR4) | | | |
241 /// | 0x24 | Base address #5 (BAR5) | | | |
242 /// | 0x28 | Cardbus CIS Pointer | | | |
243 /// | 0x2C | Subsystem ID | | Subsystem Vendor ID | |
244 /// | 0x30 | Expansion ROM base address | | | |
245 /// | 0x34 | Reserved | | | Capabilities Pointer |
246 /// | 0x38 | Reserved | | | |
247 /// | 0x3C | Max latency | Min Grant | Interrupt PIN | Interrupt Line |
248 pub enum HeaderType00: u16 {
249 DEVICE_VENDOR = 0x00,
250 STATUS_COMMAND = 0x04,
251 CLASS_REVISION = 0x08,
252 BIST_HEADER = 0x0C,
253 BAR0 = 0x10,
254 BAR1 = 0x14,
255 BAR2 = 0x18,
256 BAR3 = 0x1C,
257 BAR4 = 0x20,
258 BAR5 = 0x24,
259 CARDBUS_CIS_PTR = 0x28,
260 SUBSYSTEM_ID = 0x2C,
261 EXPANSION_ROM_BASE = 0x30,
262 RESERVED_CAP_PTR = 0x34,
263 RESERVED = 0x38,
264 LATENCY_INTERRUPT = 0x3C,
265 }
266 }
267
268 pub const HEADER_TYPE_00_SIZE: u16 = 0x40;
269
270 /// The BIST / Header Type / Latency Timer / Cache Line Size DWORD
271 /// at config space offset 0x0C.
272 ///
273 /// | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-0 |
274 /// |------------|-------------|-----------------|------------------|
275 /// | BIST | Header Type | Latency Timer | Cache Line Size |
276 #[bitfield(u32)]
277 pub struct BistHeader {
278 pub cache_line_size: u8,
279 pub latency_timer: u8,
280 /// Header layout type (0 = standard, 1 = PCI-to-PCI bridge).
281 #[bits(7)]
282 pub header_type: u8,
283 /// When set, the device is part of a multi-function package.
284 pub multi_function: bool,
285 pub bist: u8,
286 }
287
288 open_enum::open_enum! {
289 /// Offsets into the type 01h configuration space header.
290 ///
291 /// Table pulled from <https://wiki.osdev.org/PCI>
292 ///
293 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-0 |
294 /// |--------|----------------------------------|------------------------|--------------------------|--------------------- |
295 /// | 0x0 | Device ID | | Vendor ID | |
296 /// | 0x4 | Status | | Command | |
297 /// | 0x8 | Class code | | | Revision ID |
298 /// | 0xC | BIST | Header Type | Latency Timer | Cache Line Size |
299 /// | 0x10 | Base address #0 (BAR0) | | | |
300 /// | 0x14 | Base address #1 (BAR1) | | | |
301 /// | 0x18 | Secondary Latency Timer | Subordinate Bus Number | Secondary Bus Number | Primary Bus Number |
302 /// | 0x1C | Secondary Status | | I/O Limit | I/O Base |
303 /// | 0x20 | Memory Limit | | Memory Base | |
304 /// | 0x24 | Prefetchable Memory Limit | | Prefetchable Memory Base | |
305 /// | 0x28 | Prefetchable Base Upper 32 Bits | | | |
306 /// | 0x2C | Prefetchable Limit Upper 32 Bits | | | |
307 /// | 0x30 | I/O Limit Upper 16 Bits | | I/O Base Upper 16 Bits | |
308 /// | 0x34 | Reserved | | | Capabilities Pointer |
309 /// | 0x38 | Expansion ROM Base Address | | | |
310 /// | 0x3C | Bridge Control | | Interrupt PIN | Interrupt Line |
311 pub enum HeaderType01: u16 {
312 DEVICE_VENDOR = 0x00,
313 STATUS_COMMAND = 0x04,
314 CLASS_REVISION = 0x08,
315 BIST_HEADER = 0x0C,
316 BAR0 = 0x10,
317 BAR1 = 0x14,
318 LATENCY_BUS_NUMBERS = 0x18,
319 SEC_STATUS_IO_RANGE = 0x1C,
320 MEMORY_RANGE = 0x20,
321 PREFETCH_RANGE = 0x24,
322 PREFETCH_BASE_UPPER = 0x28,
323 PREFETCH_LIMIT_UPPER = 0x2C,
324 IO_RANGE_UPPER = 0x30,
325 RESERVED_CAP_PTR = 0x34,
326 EXPANSION_ROM_BASE = 0x38,
327 BRDIGE_CTRL_INTERRUPT = 0x3C,
328 }
329 }
330
331 pub const HEADER_TYPE_01_SIZE: u16 = 0x40;
332
333 /// The low 4 bits of the memory base/limit registers are reserved.
334 pub const MEMORY_BASE_LIMIT_ADDRESS_MASK: u16 = 0xFFF0;
335
336 /// The low bit of the prefetchable memory base/limit registers indicates
337 /// whether the range is 64-bit or 32-bit.
338 pub const PREFETCH_MEMORY_BASE_LIMIT_64BIT: u16 = 0x1;
339
340 /// BAR in-band encoding bits.
341 ///
342 /// The low bits of the BAR are not actually part of the address.
343 /// Instead, they are used to in-band encode various bits of
344 /// metadata about the BAR, and are masked off when determining the
345 /// actual address.
346 #[bitfield(u32)]
347 pub struct BarEncodingBits {
348 pub use_pio: bool,
349
350 _reserved: bool,
351
352 /// False indicates 32 bit.
353 /// Only used in MMIO
354 pub type_64_bit: bool,
355 pub prefetchable: bool,
356
357 #[bits(28)]
358 _reserved2: u32,
359 }
360
361 /// Command Register
362 #[derive(Inspect)]
363 #[bitfield(u16)]
364 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
365 pub struct Command {
366 pub pio_enabled: bool,
367 pub mmio_enabled: bool,
368 pub bus_master: bool,
369 pub special_cycles: bool,
370 pub enable_memory_write_invalidate: bool,
371 pub vga_palette_snoop: bool,
372 pub parity_error_response: bool,
373 /// must be 0
374 #[bits(1)]
375 _reserved: u16,
376 pub enable_serr: bool,
377 pub enable_fast_b2b: bool,
378 pub intx_disable: bool,
379 #[bits(5)]
380 _reserved2: u16,
381 }
382
383 /// Status Register
384 #[bitfield(u16)]
385 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
386 pub struct Status {
387 #[bits(3)]
388 _reserved: u16,
389 pub interrupt_status: bool,
390 pub capabilities_list: bool,
391 pub capable_mhz_66: bool,
392 _reserved2: bool,
393 pub capable_fast_b2b: bool,
394 pub err_master_parity: bool,
395
396 #[bits(2)]
397 pub devsel: DevSel,
398
399 pub abort_target_signaled: bool,
400 pub abort_target_received: bool,
401 pub abort_master_received: bool,
402 pub err_signaled: bool,
403 pub err_detected_parity: bool,
404 }
405
406 #[derive(Debug)]
407 #[repr(u16)]
408 pub enum DevSel {
409 Fast = 0b00,
410 Medium = 0b01,
411 Slow = 0b10,
412 }
413
414 impl DevSel {
415 const fn from_bits(bits: u16) -> Self {
416 match bits {
417 0b00 => DevSel::Fast,
418 0b01 => DevSel::Medium,
419 0b10 => DevSel::Slow,
420 _ => unreachable!(),
421 }
422 }
423
424 const fn into_bits(self) -> u16 {
425 self as u16
426 }
427 }
428}
429
430/// Capabilities
431pub mod caps {
432 open_enum::open_enum! {
433 /// Capability IDs
434 ///
435 /// Sources: PCI 2.3 Spec - Appendix H
436 ///
437 /// NOTE: this is a non-exhaustive list, so don't be afraid to add new
438 /// variants on an as-needed basis!
439 pub enum CapabilityId: u8 {
440 #![expect(missing_docs)] // self explanatory variants
441 POWER_MANAGEMENT = 0x01,
442 MSI = 0x05,
443 VENDOR_SPECIFIC = 0x09,
444 PCI_EXPRESS = 0x10,
445 MSIX = 0x11,
446 ADVANCED_FEATURES = 0x13,
447 }
448 }
449
450 open_enum::open_enum! {
451
452 /// PCIe Extended Capability IDs (offsets 0x100+ in config space).
453 ///
454 /// Sources: PCI Express Base Specification
455 ///
456 /// NOTE: this is a non-exhaustive list, so don't be afraid to add new
457 /// variants on an as-needed basis!
458 pub enum ExtendedCapabilityId: u16 {
459 #![expect(missing_docs)] // self explanatory variants
460 ACS = 0x0D,
461 ARI = 0x0E,
462 SRIOV = 0x10,
463 REBAR = 0x15,
464 DVSEC = 0x23,
465 SIOV = 0x38,
466 }
467 }
468
469 /// Starting offset of the PCIe extended capability region in config space.
470 pub const EXT_CAP_START: u16 = 0x100;
471 /// Ending offset (exclusive) of the PCIe extended capability region in config space.
472 pub const EXT_CAP_END: u16 = 0x1000;
473 /// Ending offset (exclusive) of the common config header region.
474 pub const COMMON_HEADER_END: u16 = 0x40;
475
476 /// Conventional PCI Advanced Features capability.
477 #[expect(missing_docs)]
478 pub mod advanced_features {
479 use bitfield_struct::bitfield;
480
481 open_enum::open_enum! {
482 pub enum CapabilityRegister: u16 {
483 HEADER = 0x00,
484 CONTROL_STATUS = 0x04,
485 }
486 }
487
488 #[bitfield(u32)]
489 pub struct Header {
490 #[bits(8)]
491 pub capability_id: u8,
492 #[bits(8)]
493 pub next_pointer: u8,
494 #[bits(8)]
495 pub length: u8,
496 #[bits(8)]
497 pub capabilities: u8,
498 }
499
500 #[bitfield(u8)]
501 pub struct Capabilities {
502 pub transactions_pending: bool,
503 pub function_level_reset: bool,
504 #[bits(6)]
505 _reserved: u8,
506 }
507
508 #[bitfield(u8)]
509 pub struct Control {
510 pub initiate_function_level_reset: bool,
511 #[bits(7)]
512 _reserved: u8,
513 }
514 }
515
516 /// MSI
517 #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
518 pub mod msi {
519 open_enum::open_enum! {
520 /// Offsets into the MSI Capability Header
521 ///
522 /// Based on PCI Local Bus Specification Rev 3.0, Section 6.8.1
523 ///
524 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-0 |
525 /// |-----------|---------------|---------------|---------------|-----------------------|
526 /// | Cap + 0x0 | Message Control | Next Pointer | Capability ID (0x05) |
527 /// | Cap + 0x4 | Message Address (32-bit or lower 32-bit of 64-bit) |
528 /// | Cap + 0x8 | Message Address Upper 32-bit (64-bit capable only) |
529 /// | Cap + 0xC | Message Data | | | |
530 /// | Cap + 0x10| Mask Bits (Per-vector masking capable only) |
531 /// | Cap + 0x14| Pending Bits (Per-vector masking capable only) |
532 pub enum MsiCapabilityHeader: u16 {
533 CONTROL_CAPS = 0x00,
534 MSG_ADDR_LO = 0x04,
535 MSG_ADDR_HI = 0x08,
536 MSG_DATA_32 = 0x08, // For 32-bit address capable
537 MSG_DATA_64 = 0x0C, // For 64-bit address capable
538 MASK_BITS = 0x10, // 64-bit + per-vector masking
539 PENDING_BITS = 0x14, // 64-bit + per-vector masking
540 }
541 }
542 }
543
544 /// MSI-X
545 #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
546 pub mod msix {
547 open_enum::open_enum! {
548 /// Offsets into the MSI-X Capability Header
549 ///
550 /// Table pulled from <https://wiki.osdev.org/PCI>
551 ///
552 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-3 | Bits 2-0 |
553 /// |-----------|--------------------|------------|--------------|----------------------|----------|
554 /// | Cap + 0x0 | Message Control | | Next Pointer | Capability ID (0x11) | |
555 /// | Cap + 0x4 | Table Offset | | | | BIR |
556 /// | Cap + 0x8 | Pending Bit Offset | | | | BIR |
557 pub enum MsixCapabilityHeader: u16 {
558 CONTROL_CAPS = 0x00,
559 OFFSET_TABLE = 0x04,
560 OFFSET_PBA = 0x08,
561 }
562 }
563
564 open_enum::open_enum! {
565 /// Offsets into a single MSI-X Table Entry
566 pub enum MsixTableEntryIdx: u64 {
567 MSG_ADDR_LO = 0x00,
568 MSG_ADDR_HI = 0x04,
569 MSG_DATA = 0x08,
570 VECTOR_CTL = 0x0C,
571 }
572 }
573 }
574
575 /// PCI Express
576 #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
577 pub mod pci_express {
578 use bitfield_struct::bitfield;
579 use inspect::Inspect;
580 use zerocopy::FromBytes;
581 use zerocopy::Immutable;
582 use zerocopy::IntoBytes;
583 use zerocopy::KnownLayout;
584
585 open_enum::open_enum! {
586 /// PCIe Link Speed encoding values for use in Link Capabilities and other registers.
587 ///
588 /// Values are defined in PCIe Base Specification for the Max Link Speed field
589 /// in Link Capabilities Register and similar fields.
590 #[derive(Inspect)]
591 #[inspect(debug)]
592 pub enum LinkSpeed: u32 {
593 #![allow(non_upper_case_globals)]
594 /// 2.5 GT/s link speed
595 Speed2_5GtS = 0b0001,
596 /// 5.0 GT/s link speed
597 Speed5_0GtS = 0b0010,
598 /// 8.0 GT/s link speed
599 Speed8_0GtS = 0b0011,
600 /// 16.0 GT/s link speed
601 Speed16_0GtS = 0b0100,
602 /// 32.0 GT/s link speed
603 Speed32_0GtS = 0b0101,
604 /// 64.0 GT/s link speed
605 Speed64_0GtS = 0b0110,
606 }
607 }
608
609 impl LinkSpeed {
610 pub const fn from_bits(bits: u32) -> Self {
611 Self(bits)
612 }
613
614 pub const fn into_bits(self) -> u32 {
615 self.0
616 }
617 }
618
619 open_enum::open_enum! {
620 /// PCIe Supported Link Speeds Vector encoding values for use in Link Capabilities 2 register.
621 ///
622 /// Values are defined in PCIe Base Specification for the Supported Link Speeds Vector field
623 /// in Link Capabilities 2 Register. Each bit represents support for a specific generation.
624 #[derive(Inspect)]
625 #[inspect(debug)]
626 pub enum SupportedLinkSpeedsVector: u32 {
627 #![allow(non_upper_case_globals)]
628 /// Support up to Gen 1 (2.5 GT/s)
629 UpToGen1 = 0b0000001,
630 /// Support up to Gen 2 (5.0 GT/s)
631 UpToGen2 = 0b0000011,
632 /// Support up to Gen 3 (8.0 GT/s)
633 UpToGen3 = 0b0000111,
634 /// Support up to Gen 4 (16.0 GT/s)
635 UpToGen4 = 0b0001111,
636 /// Support up to Gen 5 (32.0 GT/s)
637 UpToGen5 = 0b0011111,
638 /// Support up to Gen 6 (64.0 GT/s)
639 UpToGen6 = 0b0111111,
640 }
641 }
642
643 impl SupportedLinkSpeedsVector {
644 pub const fn from_bits(bits: u32) -> Self {
645 Self(bits)
646 }
647
648 pub const fn into_bits(self) -> u32 {
649 self.0
650 }
651 }
652
653 /// PCIe max TLP prefix values for use in Device Capabilities 2.
654 ///
655 /// Values are defined in PCIe Base Specification for the Max End-End TLP Prefixes
656 /// field in Device Capabilities 2 Register and similar fields.
657 #[derive(Copy, Clone, Debug)]
658 #[repr(u32)]
659 pub enum MaxEndEndTlpPrefixes {
660 /// 1 End-End TLP Prefix / OHC-E1
661 One = 0b01,
662 /// 2 End-End TLP Prefixes / OHC-E2
663 Two = 0b10,
664 /// 3 End-End TLP Prefixes / OHC-E4
665 Three = 0b11,
666 /// 4 End-End TLP Prefixes / OHC-E4
667 Four = 0b00,
668 }
669
670 impl MaxEndEndTlpPrefixes {
671 pub(crate) const fn from_bits(bits: u32) -> Self {
672 match bits {
673 0b01 => MaxEndEndTlpPrefixes::One,
674 0b10 => MaxEndEndTlpPrefixes::Two,
675 0b11 => MaxEndEndTlpPrefixes::Three,
676 0b00 => MaxEndEndTlpPrefixes::Four,
677 _ => unreachable!(),
678 }
679 }
680
681 pub const fn into_bits(self) -> u32 {
682 self as u32
683 }
684 }
685
686 open_enum::open_enum! {
687 /// PCIe Link Width encoding values for use in Link Capabilities and other registers.
688 ///
689 /// Values are defined in PCIe Base Specification for the Max Link Width field
690 /// in Link Capabilities Register and similar fields.
691 #[derive(Inspect)]
692 #[inspect(debug)]
693 pub enum LinkWidth: u32 {
694 /// x1 link width
695 X1 = 0b000001,
696 /// x2 link width
697 X2 = 0b000010,
698 /// x4 link width
699 X4 = 0b000100,
700 /// x8 link width
701 X8 = 0b001000,
702 /// x16 link width
703 X16 = 0b010000,
704 }
705 }
706
707 impl LinkWidth {
708 pub const fn from_bits(bits: u32) -> Self {
709 Self(bits)
710 }
711
712 pub const fn into_bits(self) -> u32 {
713 self.0
714 }
715 }
716
717 open_enum::open_enum! {
718 /// Offsets into the PCI Express Capability Header
719 ///
720 /// Table pulled from PCI Express Base Specification Rev. 3.0
721 ///
722 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-0 |
723 /// |-----------|------------------|----------------- |------------------|----------------------|
724 /// | Cap + 0x0 | PCI Express Capabilities Register | Next Pointer | Capability ID (0x10) |
725 /// | Cap + 0x4 | Device Capabilities Register |
726 /// | Cap + 0x8 | Device Status | Device Control |
727 /// | Cap + 0xC | Link Capabilities Register |
728 /// | Cap + 0x10| Link Status | Link Control |
729 /// | Cap + 0x14| Slot Capabilities Register |
730 /// | Cap + 0x18| Slot Status | Slot Control |
731 /// | Cap + 0x1C| Root Capabilities| Root Control |
732 /// | Cap + 0x20| Root Status Register |
733 /// | Cap + 0x24| Device Capabilities 2 Register |
734 /// | Cap + 0x28| Device Status 2 | Device Control 2 |
735 /// | Cap + 0x2C| Link Capabilities 2 Register |
736 /// | Cap + 0x30| Link Status 2 | Link Control 2 |
737 /// | Cap + 0x34| Slot Capabilities 2 Register |
738 /// | Cap + 0x38| Slot Status 2 | Slot Control 2 |
739 pub enum PciExpressCapabilityHeader: u16 {
740 PCIE_CAPS = 0x00,
741 DEVICE_CAPS = 0x04,
742 DEVICE_CTL_STS = 0x08,
743 LINK_CAPS = 0x0C,
744 LINK_CTL_STS = 0x10,
745 SLOT_CAPS = 0x14,
746 SLOT_CTL_STS = 0x18,
747 ROOT_CTL_CAPS = 0x1C,
748 ROOT_STS = 0x20,
749 DEVICE_CAPS_2 = 0x24,
750 DEVICE_CTL_STS_2 = 0x28,
751 LINK_CAPS_2 = 0x2C,
752 LINK_CTL_STS_2 = 0x30,
753 SLOT_CAPS_2 = 0x34,
754 SLOT_CTL_STS_2 = 0x38,
755 }
756 }
757
758 /// PCI Express Capabilities Register
759 #[bitfield(u16)]
760 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
761 pub struct PciExpressCapabilities {
762 #[bits(4)]
763 pub capability_version: u16,
764 #[bits(4)]
765 pub device_port_type: DevicePortType,
766 pub slot_implemented: bool,
767 #[bits(5)]
768 pub interrupt_message_number: u16,
769 pub _undefined: bool,
770 pub flit_mode_supported: bool,
771 }
772
773 open_enum::open_enum! {
774 #[derive(Inspect)]
775 #[inspect(debug)]
776 pub enum DevicePortType: u16 {
777 #![allow(non_upper_case_globals)]
778 Endpoint = 0b0000,
779 RootPort = 0b0100,
780 UpstreamSwitchPort = 0b0101,
781 DownstreamSwitchPort = 0b0110,
782 }
783 }
784
785 impl DevicePortType {
786 const fn from_bits(bits: u16) -> Self {
787 Self(bits)
788 }
789
790 const fn into_bits(self) -> u16 {
791 self.0
792 }
793 }
794
795 /// Device Capabilities Register (From the 6.4 spec)
796 #[bitfield(u32)]
797 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
798 pub struct DeviceCapabilities {
799 #[bits(3)]
800 pub max_payload_size: u32,
801 #[bits(2)]
802 pub phantom_functions: u32,
803 pub ext_tag_field: bool,
804 #[bits(3)]
805 pub endpoint_l0s_latency: u32,
806 #[bits(3)]
807 pub endpoint_l1_latency: u32,
808 #[bits(3)]
809 _reserved1: u32,
810 pub role_based_error: bool,
811 pub err_cor_subclass_capable: bool,
812 pub rx_mps_fixed: bool,
813 #[bits(8)]
814 pub captured_slot_power_limit: u32,
815 #[bits(2)]
816 pub captured_slot_power_scale: u32,
817 pub function_level_reset: bool,
818 pub mixed_mps_supported: bool,
819 pub tee_io_supported: bool,
820 _reserved3: bool,
821 }
822
823 /// Device Control Register
824 #[bitfield(u16)]
825 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
826 pub struct DeviceControl {
827 pub correctable_error_reporting_enable: bool,
828 pub non_fatal_error_reporting_enable: bool,
829 pub fatal_error_reporting_enable: bool,
830 pub unsupported_request_reporting_enable: bool,
831 pub enable_relaxed_ordering: bool,
832 #[bits(3)]
833 pub max_payload_size: u16,
834 pub extended_tag_enable: bool,
835 pub phantom_functions_enable: bool,
836 pub aux_power_pm_enable: bool,
837 pub enable_no_snoop: bool,
838 #[bits(3)]
839 pub max_read_request_size: u16,
840 pub initiate_function_level_reset: bool,
841 }
842
843 /// Device Status Register
844 #[bitfield(u16)]
845 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
846 pub struct DeviceStatus {
847 pub correctable_error_detected: bool,
848 pub non_fatal_error_detected: bool,
849 pub fatal_error_detected: bool,
850 pub unsupported_request_detected: bool,
851 pub aux_power_detected: bool,
852 pub transactions_pending: bool,
853 #[bits(10)]
854 _reserved: u16,
855 }
856
857 /// Link Capabilities Register
858 #[bitfield(u32)]
859 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
860 pub struct LinkCapabilities {
861 #[bits(4)]
862 pub max_link_speed: LinkSpeed,
863 #[bits(6)]
864 pub max_link_width: LinkWidth,
865 #[bits(2)]
866 pub aspm_support: u32,
867 #[bits(3)]
868 pub l0s_exit_latency: u32,
869 #[bits(3)]
870 pub l1_exit_latency: u32,
871 pub clock_power_management: bool,
872 pub surprise_down_error_reporting: bool,
873 pub data_link_layer_link_active_reporting: bool,
874 pub link_bandwidth_notification_capability: bool,
875 pub aspm_optionality_compliance: bool,
876 #[bits(1)]
877 _reserved: u32,
878 #[bits(8)]
879 pub port_number: u32,
880 }
881
882 /// Link Control Register
883 #[bitfield(u16)]
884 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
885 pub struct LinkControl {
886 #[bits(2)]
887 pub aspm_control: u16,
888 pub ptm_propagation_delay_adaptation_interpretation_b: bool,
889 #[bits(1)]
890 pub read_completion_boundary: u16,
891 pub link_disable: bool,
892 pub retrain_link: bool,
893 pub common_clock_configuration: bool,
894 pub extended_synch: bool,
895 pub enable_clock_power_management: bool,
896 pub hardware_autonomous_width_disable: bool,
897 pub link_bandwidth_management_interrupt_enable: bool,
898 pub link_autonomous_bandwidth_interrupt_enable: bool,
899 #[bits(1)]
900 pub sris_clocking: u16,
901 pub flit_mode_disable: bool,
902 #[bits(2)]
903 pub drs_signaling_control: u16,
904 }
905
906 /// Link Status Register
907 #[bitfield(u16)]
908 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
909 pub struct LinkStatus {
910 #[bits(4)]
911 pub current_link_speed: LinkSpeed,
912 #[bits(6)]
913 pub negotiated_link_width: LinkWidth,
914 #[bits(1)]
915 _reserved: u16,
916 pub link_training: bool,
917 pub slot_clock_configuration: bool,
918 pub data_link_layer_link_active: bool,
919 pub link_bandwidth_management_status: bool,
920 pub link_autonomous_bandwidth_status: bool,
921 }
922
923 /// Slot Capabilities Register
924 #[bitfield(u32)]
925 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
926 pub struct SlotCapabilities {
927 pub attention_button_present: bool,
928 pub power_controller_present: bool,
929 pub mrl_sensor_present: bool,
930 pub attention_indicator_present: bool,
931 pub power_indicator_present: bool,
932 pub hot_plug_surprise: bool,
933 pub hot_plug_capable: bool,
934 #[bits(8)]
935 pub slot_power_limit_value: u32,
936 #[bits(2)]
937 pub slot_power_limit_scale: u32,
938 pub electromechanical_interlock_present: bool,
939 pub no_command_completed_support: bool,
940 #[bits(13)]
941 pub physical_slot_number: u32,
942 }
943
944 /// Slot Control Register
945 #[bitfield(u16)]
946 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
947 pub struct SlotControl {
948 pub attention_button_pressed_enable: bool,
949 pub power_fault_detected_enable: bool,
950 pub mrl_sensor_changed_enable: bool,
951 pub presence_detect_changed_enable: bool,
952 pub command_completed_interrupt_enable: bool,
953 pub hot_plug_interrupt_enable: bool,
954 #[bits(2)]
955 pub attention_indicator_control: u16,
956 #[bits(2)]
957 pub power_indicator_control: u16,
958 pub power_controller_control: bool,
959 pub electromechanical_interlock_control: bool,
960 pub data_link_layer_state_changed_enable: bool,
961 pub auto_slot_power_limit_enable: bool,
962 pub in_band_pd_disable: bool,
963 #[bits(1)]
964 _reserved: u16,
965 }
966
967 /// Slot Status Register
968 #[bitfield(u16)]
969 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
970 pub struct SlotStatus {
971 pub attention_button_pressed: bool,
972 pub power_fault_detected: bool,
973 pub mrl_sensor_changed: bool,
974 pub presence_detect_changed: bool,
975 pub command_completed: bool,
976 #[bits(1)]
977 pub mrl_sensor_state: u16,
978 #[bits(1)]
979 pub presence_detect_state: u16,
980 #[bits(1)]
981 pub electromechanical_interlock_status: u16,
982 pub data_link_layer_state_changed: bool,
983 #[bits(7)]
984 _reserved: u16,
985 }
986
987 /// Root Control Register
988 #[bitfield(u16)]
989 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
990 pub struct RootControl {
991 pub system_error_on_correctable_error_enable: bool,
992 pub system_error_on_non_fatal_error_enable: bool,
993 pub system_error_on_fatal_error_enable: bool,
994 pub pme_interrupt_enable: bool,
995 pub crs_software_visibility_enable: bool,
996 pub no_nfm_subtree_below_this_root_port: bool,
997 #[bits(10)]
998 _reserved: u16,
999 }
1000
1001 /// Root Capabilities Register
1002 #[bitfield(u16)]
1003 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1004 pub struct RootCapabilities {
1005 pub crs_software_visibility: bool,
1006 #[bits(15)]
1007 _reserved: u16,
1008 }
1009
1010 /// Root Status Register
1011 #[bitfield(u32)]
1012 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1013 pub struct RootStatus {
1014 #[bits(16)]
1015 pub pme_requester_id: u32,
1016 pub pme_status: bool,
1017 pub pme_pending: bool,
1018 #[bits(14)]
1019 _reserved: u32,
1020 }
1021
1022 /// Device Capabilities 2 Register
1023 #[bitfield(u32)]
1024 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1025 pub struct DeviceCapabilities2 {
1026 #[bits(4)]
1027 pub completion_timeout_ranges_supported: u32,
1028 pub completion_timeout_disable_supported: bool,
1029 pub ari_forwarding_supported: bool,
1030 pub atomic_op_routing_supported: bool,
1031 pub atomic_op_32_bit_completer_supported: bool,
1032 pub atomic_op_64_bit_completer_supported: bool,
1033 pub cas_128_bit_completer_supported: bool,
1034 pub no_ro_enabled_pr_pr_passing: bool,
1035 pub ltr_mechanism_supported: bool,
1036 #[bits(2)]
1037 pub tph_completer_supported: u32,
1038 #[bits(2)]
1039 _reserved: u32,
1040 pub ten_bit_tag_completer_supported: bool,
1041 pub ten_bit_tag_requester_supported: bool,
1042 #[bits(2)]
1043 pub obff_supported: u32,
1044 pub extended_fmt_field_supported: bool,
1045 pub end_end_tlp_prefix_supported: bool,
1046 #[bits(2)]
1047 pub max_end_end_tlp_prefixes: MaxEndEndTlpPrefixes,
1048 #[bits(2)]
1049 pub emergency_power_reduction_supported: u32,
1050 pub emergency_power_reduction_init_required: bool,
1051 #[bits(1)]
1052 _reserved: u32,
1053 pub dmwr_completer_supported: bool,
1054 #[bits(2)]
1055 pub dmwr_lengths_supported: u32,
1056 pub frs_supported: bool,
1057 }
1058
1059 /// Device Control 2 Register
1060 #[bitfield(u16)]
1061 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1062 pub struct DeviceControl2 {
1063 #[bits(4)]
1064 pub completion_timeout_value: u16,
1065 pub completion_timeout_disable: bool,
1066 pub ari_forwarding_enable: bool,
1067 pub atomic_op_requester_enable: bool,
1068 pub atomic_op_egress_blocking: bool,
1069 pub ido_request_enable: bool,
1070 pub ido_completion_enable: bool,
1071 pub ltr_mechanism_enable: bool,
1072 pub emergency_power_reduction_request: bool,
1073 pub ten_bit_tag_requester_enable: bool,
1074 #[bits(2)]
1075 pub obff_enable: u16,
1076 pub end_end_tlp_prefix_blocking: bool,
1077 }
1078
1079 /// Device Status 2 Register
1080 #[bitfield(u16)]
1081 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1082 pub struct DeviceStatus2 {
1083 #[bits(16)]
1084 _reserved: u16,
1085 }
1086
1087 /// Link Capabilities 2 Register
1088 #[bitfield(u32)]
1089 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1090 pub struct LinkCapabilities2 {
1091 #[bits(1)]
1092 _reserved: u32,
1093 #[bits(7)]
1094 pub supported_link_speeds_vector: SupportedLinkSpeedsVector,
1095 pub crosslink_supported: bool,
1096 #[bits(7)]
1097 pub lower_skp_os_generation_supported_speeds_vector: u32,
1098 #[bits(7)]
1099 pub lower_skp_os_reception_supported_speeds_vector: u32,
1100 pub retimer_presence_detect_supported: bool,
1101 pub two_retimers_presence_detect_supported: bool,
1102 #[bits(6)]
1103 _reserved: u32,
1104 pub drs_supported: bool,
1105 }
1106
1107 /// Link Control 2 Register
1108 #[bitfield(u16)]
1109 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1110 pub struct LinkControl2 {
1111 #[bits(4)]
1112 pub target_link_speed: LinkSpeed,
1113 pub enter_compliance: bool,
1114 pub hardware_autonomous_speed_disable: bool,
1115 #[bits(1)]
1116 pub selectable_de_emphasis: u16,
1117 #[bits(3)]
1118 pub transmit_margin: u16,
1119 pub enter_modified_compliance: bool,
1120 pub compliance_sos: bool,
1121 #[bits(4)]
1122 pub compliance_preset_de_emphasis: u16,
1123 }
1124
1125 /// Link Status 2 Register
1126 #[bitfield(u16)]
1127 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1128 pub struct LinkStatus2 {
1129 #[bits(1)]
1130 pub current_de_emphasis_level: u16,
1131 pub equalization_8gts_complete: bool,
1132 pub equalization_8gts_phase_1_successful: bool,
1133 pub equalization_8gts_phase_2_successful: bool,
1134 pub equalization_8gts_phase_3_successful: bool,
1135 pub link_equalization_request_8gts: bool,
1136 pub retimer_presence_detected: bool,
1137 pub two_retimers_presence_detected: bool,
1138 #[bits(2)]
1139 pub crosslink_resolution: u16,
1140 pub flit_mode_status: bool,
1141 #[bits(1)]
1142 _reserved: u16,
1143 #[bits(3)]
1144 pub downstream_component_presence: u16,
1145 pub drs_message_received: bool,
1146 }
1147
1148 /// Slot Capabilities 2 Register
1149 #[bitfield(u32)]
1150 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1151 pub struct SlotCapabilities2 {
1152 pub in_band_pd_disable_supported: bool,
1153 #[bits(31)]
1154 _reserved: u32,
1155 }
1156
1157 /// Slot Control 2 Register
1158 #[bitfield(u16)]
1159 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1160 pub struct SlotControl2 {
1161 #[bits(16)]
1162 _reserved: u16,
1163 }
1164
1165 /// Slot Status 2 Register
1166 #[bitfield(u16)]
1167 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1168 pub struct SlotStatus2 {
1169 #[bits(16)]
1170 _reserved: u16,
1171 }
1172 }
1173
1174 /// Access Control Services (ACS) extended capability
1175 #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
1176 pub mod acs {
1177 use bitfield_struct::bitfield;
1178 use inspect::Inspect;
1179 use zerocopy::FromBytes;
1180 use zerocopy::Immutable;
1181 use zerocopy::IntoBytes;
1182 use zerocopy::KnownLayout;
1183
1184 /// Default ACS capability mask: SV, TB, RR, CR, UF, DT (no egress control vector).
1185 pub const DEFAULT_ACS_CAP_MASK: u16 = 0x005f;
1186
1187 open_enum::open_enum! {
1188 /// Offsets into the ACS Extended Capability structure.
1189 ///
1190 /// | Offset | Bits 31-16 | Bits 15-0 |
1191 /// |-----------|---------------------------|-------------------------|
1192 /// | Ext + 0x0 | Next Cap Ptr + Version | Extended Capability ID |
1193 /// | Ext + 0x4 | ACS Control Register | ACS Capability Register |
1194 /// | Ext + 0x8 | Egress Control Vector (DWORD 0, if required) |
1195 /// | Ext + 0xC | Egress Control Vector (additional DWORDs, optional) |
1196 pub enum AcsExtendedCapabilityHeader: u16 {
1197 HEADER = 0x00,
1198 CAPS_CONTROL = 0x04,
1199 EGRESS_CONTROL_VECTOR = 0x08,
1200 }
1201 }
1202
1203 /// Access Control Services Capability register.
1204 #[bitfield(u16)]
1205 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1206 pub struct AcsCapabilities {
1207 pub source_validation: bool,
1208 pub translation_blocking: bool,
1209 pub p2p_request_redirect: bool,
1210 pub p2p_completion_redirect: bool,
1211 pub upstream_forwarding: bool,
1212 pub p2p_egress_control: bool,
1213 pub direct_translated_p2p: bool,
1214 #[bits(9)]
1215 _reserved: u16,
1216 }
1217
1218 /// Access Control Services Control register.
1219 #[bitfield(u16)]
1220 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1221 pub struct AcsControl {
1222 pub source_validation_enable: bool,
1223 pub translation_blocking_enable: bool,
1224 pub p2p_request_redirect_enable: bool,
1225 pub p2p_completion_redirect_enable: bool,
1226 pub upstream_forwarding_enable: bool,
1227 pub p2p_egress_control_enable: bool,
1228 pub direct_translated_p2p_enable: bool,
1229 #[bits(9)]
1230 _reserved: u16,
1231 }
1232 }
1233
1234 /// Designated Vendor-Specific Extended Capability (DVSEC)
1235 #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
1236 pub mod dvsec {
1237 use bitfield_struct::bitfield;
1238 use inspect::Inspect;
1239 use zerocopy::FromBytes;
1240 use zerocopy::Immutable;
1241 use zerocopy::IntoBytes;
1242 use zerocopy::KnownLayout;
1243
1244 open_enum::open_enum! {
1245 /// Offsets into the DVSEC Extended Capability structure.
1246 ///
1247 /// | Offset | Bits 31-16 | Bits 15-0 |
1248 /// |-----------|--------------------------|-------------------------|
1249 /// | Ext + 0x0 | Next Cap Ptr + Version | Extended Capability ID |
1250 /// | Ext + 0x4 | DVSEC Length + Revision | DVSEC Vendor ID |
1251 /// | Ext + 0x8 | Reserved | DVSEC ID |
1252 pub enum DvsecExtendedCapabilityHeader: u16 {
1253 HEADER = 0x00,
1254 DVSEC_HEADER1 = 0x04,
1255 DVSEC_HEADER2 = 0x08,
1256 }
1257 }
1258
1259 /// DVSEC Header 1 register.
1260 ///
1261 /// Software should qualify the DVSEC Vendor ID before interpreting the
1262 /// DVSEC Revision field.
1263 ///
1264 /// | Bits 31-20 | Bits 19-16 | Bits 15-0 |
1265 /// |--------------|-----------------|------------------|
1266 /// | DVSEC Length | DVSEC Revision | DVSEC Vendor ID |
1267 #[bitfield(u32)]
1268 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1269 pub struct DvsecHeader1 {
1270 pub dvsec_vendor_id: u16,
1271 #[bits(4)]
1272 pub dvsec_revision: u8,
1273 #[bits(12)]
1274 pub dvsec_length: u16,
1275 }
1276
1277 /// DVSEC Header 2 register.
1278 ///
1279 /// Software should qualify the DVSEC Vendor ID before interpreting the
1280 /// DVSEC ID field.
1281 ///
1282 /// | Bits 15-0 |
1283 /// |-----------|
1284 /// | DVSEC ID |
1285 #[bitfield(u16)]
1286 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
1287 pub struct DvsecHeader2 {
1288 pub dvsec_id: u16,
1289 }
1290 }
1291
1292 /// SR-IOV Extended Capability
1293 ///
1294 /// Source: PCI Express Base Specification, "Single Root I/O Virtualization"
1295 #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
1296 pub mod sriov {
1297 open_enum::open_enum! {
1298 /// Offsets into the SR-IOV Extended Capability structure.
1299 ///
1300 /// | Offset | Bits 31-16 | Bits 15-0 |
1301 /// |-----------|-------------------------|-------------------------|
1302 /// | Ext + 0x0 | Next Cap Ptr + Version | Extended Capability ID |
1303 /// | Ext + 0x4 | SR-IOV Capabilities |
1304 /// | Ext + 0x8 | SR-IOV Status | SR-IOV Control |
1305 /// | Ext + 0xC | Total VFs | Initial VFs |
1306 /// | Ext + 0x10| Function Dep Link | Num VFs |
1307 /// | Ext + 0x14| VF Stride | First VF Offset |
1308 /// | Ext + 0x18| VF Device ID | Reserved |
1309 /// | Ext + 0x1C| Supported Page Sizes |
1310 /// | Ext + 0x20| System Page Size |
1311 /// | Ext + 0x24| VF BAR0 |
1312 /// | Ext + 0x28| VF BAR1 |
1313 /// | Ext + 0x2C| VF BAR2 |
1314 /// | Ext + 0x30| VF BAR3 |
1315 /// | Ext + 0x34| VF BAR4 |
1316 /// | Ext + 0x38| VF BAR5 |
1317 /// | Ext + 0x3C| VF Migration State Array Offset |
1318 pub enum SriovExtendedCapabilityHeader: u16 {
1319 HEADER = 0x00,
1320 CAPS = 0x04,
1321 /// SR-IOV Control (bits 15:0) and SR-IOV Status (bits 31:16).
1322 CONTROL_STATUS = 0x08,
1323 INITIAL_TOTAL_VFS = 0x0C,
1324 VF_OFFSET_STRIDE = 0x14,
1325 VF_BAR0 = 0x24,
1326 }
1327 }
1328
1329 /// ARI Capable Hierarchy bit within the 16-bit SR-IOV Control register
1330 /// (at [`SriovExtendedCapabilityHeader::CONTROL_STATUS`]).
1331 ///
1332 /// Source: PCI Express Base Specification §9.4.3.3.5. Present only in
1333 /// the lowest-numbered PF of a device; Read Only Zero in other PFs.
1334 /// When Set, it hints that ARI has been enabled in the Root Port or
1335 /// Switch Downstream Port immediately above the device, allowing VFs to
1336 /// be assigned Function Numbers greater than 7 to conserve Bus Numbers.
1337 pub const SRIOV_CONTROL_ARI_CAPABLE_HIERARCHY: u16 = 1 << 4;
1338 }
1339
1340 /// Source: PCI Express Base Specification §7.8.8, "ARI Extended Capability"
1341 #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
1342 pub mod ari {
1343 open_enum::open_enum! {
1344 /// Offsets into the ARI Extended Capability structure.
1345 ///
1346 /// | Offset | Bits 31-16 | Bits 15-0 |
1347 /// |-----------|-------------------------|-------------------------|
1348 /// | Ext + 0x0 | Next Cap Ptr + Version | Extended Capability ID |
1349 /// | Ext + 0x4 | ARI Control | ARI Capability |
1350 ///
1351 /// The ARI Capability register (bits 15:0 at Ext + 0x4) holds the
1352 /// Next Function Number in bits 15:8; see
1353 /// [`ARI_CAPABILITY_NEXT_FUNCTION_SHIFT`].
1354 pub enum AriExtendedCapabilityHeader: u16 {
1355 HEADER = 0x00,
1356 CAPABILITY_CONTROL = 0x04,
1357 }
1358 }
1359
1360 /// Bit shift of the Next Function Number field within the ARI
1361 /// Capability register (bits 15:8 of the 16-bit register at
1362 /// [`AriExtendedCapabilityHeader::CAPABILITY_CONTROL`]).
1363 ///
1364 /// Source: PCI Express Base Specification §7.8.8.2. Function 0 is the
1365 /// head of a linked list of Function Numbers; a value of 0 terminates
1366 /// the list. Function Numbers may be sparse and non-sequential.
1367 pub const ARI_CAPABILITY_NEXT_FUNCTION_SHIFT: u32 = 8;
1368 }
1369}