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 PCI_EXPRESS = 0x10,
344 MSIX = 0x11,
345 }
346 }
347
348 /// MSI-X
349 #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
350 pub mod msix {
351 open_enum::open_enum! {
352 /// Offsets into the MSI-X Capability Header
353 ///
354 /// Table pulled from <https://wiki.osdev.org/PCI>
355 ///
356 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-3 | Bits 2-0 |
357 /// |-----------|--------------------|------------|--------------|----------------------|----------|
358 /// | Cap + 0x0 | Message Control | | Next Pointer | Capability ID (0x11) | |
359 /// | Cap + 0x4 | Table Offset | | | | BIR |
360 /// | Cap + 0x8 | Pending Bit Offset | | | | BIR |
361 pub enum MsixCapabilityHeader: u16 {
362 CONTROL_CAPS = 0x00,
363 OFFSET_TABLE = 0x04,
364 OFFSET_PBA = 0x08,
365 }
366 }
367
368 open_enum::open_enum! {
369 /// Offsets into a single MSI-X Table Entry
370 pub enum MsixTableEntryIdx: u16 {
371 MSG_ADDR_LO = 0x00,
372 MSG_ADDR_HI = 0x04,
373 MSG_DATA = 0x08,
374 VECTOR_CTL = 0x0C,
375 }
376 }
377 }
378
379 /// PCI Express
380 #[expect(missing_docs)] // primarily enums/structs with self-explanatory variants
381 pub mod pci_express {
382 use bitfield_struct::bitfield;
383 use inspect::Inspect;
384 use zerocopy::FromBytes;
385 use zerocopy::Immutable;
386 use zerocopy::IntoBytes;
387 use zerocopy::KnownLayout;
388
389 open_enum::open_enum! {
390 /// Offsets into the PCI Express Capability Header
391 ///
392 /// Table pulled from PCI Express Base Specification Rev. 3.0
393 ///
394 /// | Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-0 |
395 /// |-----------|------------------|----------------- |------------------|----------------------|
396 /// | Cap + 0x0 | PCI Express Capabilities Register | Next Pointer | Capability ID (0x10) |
397 /// | Cap + 0x4 | Device Capabilities Register |
398 /// | Cap + 0x8 | Device Status | Device Control |
399 pub enum PciExpressCapabilityHeader: u16 {
400 PCIE_CAPS = 0x00,
401 DEVICE_CAPS = 0x04,
402 DEVICE_CTL_STS = 0x08,
403 }
404 }
405
406 /// Device Capabilities Register (From the 6.4 spec)
407 #[bitfield(u32)]
408 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
409 pub struct DeviceCapabilities {
410 #[bits(3)]
411 pub max_payload_size: u32,
412 #[bits(2)]
413 pub phantom_functions: u32,
414 pub ext_tag_field: bool,
415 #[bits(3)]
416 pub endpoint_l0s_latency: u32,
417 #[bits(3)]
418 pub endpoint_l1_latency: u32,
419 #[bits(3)]
420 _reserved1: u32,
421 pub role_based_error: bool,
422 pub err_cor_subclass_capable: bool,
423 pub rx_mps_fixed: bool,
424 #[bits(8)]
425 pub captured_slot_power_limit: u32,
426 #[bits(2)]
427 pub captured_slot_power_scale: u32,
428 pub function_level_reset: bool,
429 pub mixed_mps_supported: bool,
430 pub tee_io_supported: bool,
431 _reserved3: bool,
432 }
433
434 /// Device Control Register
435 #[bitfield(u16)]
436 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
437 pub struct DeviceControl {
438 pub correctable_error_reporting_enable: bool,
439 pub non_fatal_error_reporting_enable: bool,
440 pub fatal_error_reporting_enable: bool,
441 pub unsupported_request_reporting_enable: bool,
442 pub enable_relaxed_ordering: bool,
443 #[bits(3)]
444 pub max_payload_size: u16,
445 pub extended_tag_enable: bool,
446 pub phantom_functions_enable: bool,
447 pub aux_power_pm_enable: bool,
448 pub enable_no_snoop: bool,
449 #[bits(3)]
450 pub max_read_request_size: u16,
451 pub initiate_function_level_reset: bool,
452 }
453
454 /// Device Status Register
455 #[bitfield(u16)]
456 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
457 pub struct DeviceStatus {
458 pub correctable_error_detected: bool,
459 pub non_fatal_error_detected: bool,
460 pub fatal_error_detected: bool,
461 pub unsupported_request_detected: bool,
462 pub aux_power_detected: bool,
463 pub transactions_pending: bool,
464 #[bits(10)]
465 _reserved: u16,
466 }
467 }
468}