1use crate::util::is_set;
5use bitfield::bitfield;
6use fidl_fuchsia_hardware_pci::{
7 Capability as FidlCapability, ExtendedCapability as FidlExtCapability,
8};
9use std::fmt;
10use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref};
11
12enum CapabilityType {
14 Null,
15 PciPowerManagement,
16 Agp,
17 VitalProductData,
18 SlotIdentification,
19 Msi,
20 CompactPciHotSwap,
21 PciX,
22 HyperTransport,
23 Vendor,
24 DebugPort,
25 CompactPciCrc,
26 PciHotplug,
27 PciBridgeSubsystemVendorId,
28 Agp8x,
29 SecureDevice,
30 PciExpress,
31 MsiX,
32 SataDataNdxCfg,
33 AdvancedFeatures,
34 EnhancedAllocation,
35 FlatteningPortalBridge,
36 Unknown(u8),
37}
38
39impl From<u8> for CapabilityType {
40 fn from(value: u8) -> Self {
41 match value {
42 0x00 => CapabilityType::Null,
43 0x01 => CapabilityType::PciPowerManagement,
44 0x02 => CapabilityType::Agp,
45 0x03 => CapabilityType::VitalProductData,
46 0x04 => CapabilityType::SlotIdentification,
47 0x05 => CapabilityType::Msi,
48 0x06 => CapabilityType::CompactPciHotSwap,
49 0x07 => CapabilityType::PciX,
50 0x08 => CapabilityType::HyperTransport,
51 0x09 => CapabilityType::Vendor,
52 0x0a => CapabilityType::DebugPort,
53 0x0b => CapabilityType::CompactPciCrc,
54 0x0c => CapabilityType::PciHotplug,
55 0x0d => CapabilityType::PciBridgeSubsystemVendorId,
56 0x0e => CapabilityType::Agp8x,
57 0x0f => CapabilityType::SecureDevice,
58 0x10 => CapabilityType::PciExpress,
59 0x11 => CapabilityType::MsiX,
60 0x12 => CapabilityType::SataDataNdxCfg,
61 0x13 => CapabilityType::AdvancedFeatures,
62 0x14 => CapabilityType::EnhancedAllocation,
63 0x15 => CapabilityType::FlatteningPortalBridge,
64 _ => CapabilityType::Unknown(value),
65 }
66 }
67}
68
69pub struct Capability<'a> {
70 offset: usize,
71 config: &'a [u8],
72 cap_type: CapabilityType,
73}
74
75impl<'a> fmt::Display for Capability<'a> {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 write!(f, "Capabilities: [{:#2x}] ", self.offset)?;
78 match self.cap_type {
79 CapabilityType::Null => write!(f, "Null"),
80 CapabilityType::PciPowerManagement => write!(f, "PCI Power Management"),
81 CapabilityType::Agp => write!(f, "AGP"),
82 CapabilityType::VitalProductData => write!(f, "Vital Product Data"),
83 CapabilityType::SlotIdentification => write!(f, "Slot Identification"),
84 CapabilityType::Msi => self.msi(f),
85 CapabilityType::CompactPciHotSwap => write!(f, "CompactPCI Hotswap"),
86 CapabilityType::PciX => write!(f, "PCI-X"),
87 CapabilityType::HyperTransport => write!(f, "HyperTransport"),
88 CapabilityType::Vendor => self.vendor(f),
89 CapabilityType::DebugPort => write!(f, "Debug Port"),
90 CapabilityType::CompactPciCrc => write!(f, "CompactPCI CRC"),
91 CapabilityType::PciHotplug => write!(f, "PCI Hotplug"),
92 CapabilityType::PciBridgeSubsystemVendorId => write!(f, "PCI Bridge Subsystem VID"),
93 CapabilityType::Agp8x => write!(f, "AGP 8x"),
94 CapabilityType::SecureDevice => write!(f, "Secure Device"),
95 CapabilityType::PciExpress => self.pci_express(f),
96 CapabilityType::MsiX => self.msi_x(f),
97 CapabilityType::SataDataNdxCfg => write!(f, "SATA Data Ndx Config"),
98 CapabilityType::AdvancedFeatures => write!(f, "Advanced Features"),
99 CapabilityType::EnhancedAllocation => write!(f, "Enhanced Allocations"),
100 CapabilityType::FlatteningPortalBridge => write!(f, "Flattening Portal Bridge"),
101 CapabilityType::Unknown(id) => write!(f, "Unknown Capability (id = {:#2x})", id),
102 }
103 }
104}
105
106impl<'a> Capability<'a> {
107 pub fn new(capability: &'a FidlCapability, config: &'a [u8]) -> Self {
108 Capability {
109 offset: capability.offset as usize,
110 config,
111 cap_type: CapabilityType::from(capability.id),
112 }
113 }
114
115 fn msi(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 let control = MsiControl(
119 ((self.config[self.offset + 3] as u16) << 8) | self.config[self.offset + 2] as u16,
120 );
121 write!(
122 f,
123 "MSI: Enable{} Count={}/{} Maskable{} 64bit{}\n",
124 is_set(control.enable()),
125 msi_mms_to_value(control.mms_enabled()),
126 msi_mms_to_value(control.mms_capable()),
127 is_set(control.pvm_capable()),
128 is_set(control.can_be_64bit())
129 )?;
130
131 if control.can_be_64bit() {
132 let (msi, _) = Ref::<_, Msi64Capability>::from_prefix(
133 &self.config[self.offset..self.config.len()],
134 )
135 .unwrap();
136 write!(
137 f,
138 "\t\tAddress: {:#010x} {:#08x} Data: {:#06x}",
139 { msi.address_upper },
140 { msi.address },
141 { msi.data }
142 )
143 } else {
144 let (msi, _) = Ref::<_, Msi32Capability>::from_prefix(
145 &self.config[self.offset..self.config.len()],
146 )
147 .unwrap();
148 write!(f, "\t\tAddress: {:#010x} Data: {:#06x}", { msi.address }, { msi.data })
149 }
150 }
151
152 fn msi_x(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 let (msix, _) =
154 Ref::<_, MsixCapability>::from_prefix(&self.config[self.offset..self.config.len()])
155 .unwrap();
156 let control = MsixControl(msix.control);
157 let table = MsixBarField(msix.table);
158 let pba = MsixBarField(msix.pba);
159 write!(
160 f,
161 "MSI-X: Enable{} Count={} Masked{} TBIR={} TOff={:#x} PBIR={} POff={:#x}",
162 is_set(control.enable()),
163 control.table_size() + 1,
164 is_set(control.function_mask()),
165 table.bir(),
166 table.offset() << 3,
167 pba.bir(),
168 pba.offset() << 3,
169 )
170 }
171
172 fn pci_express(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 let (pcie, _) = Ref::<_, PciExpressCapability>::from_prefix(
174 &self.config[self.offset..self.config.len()],
175 )
176 .unwrap();
177 let pcie_capabilities = PcieCapabilitiesField(pcie.pcie_capabilities);
179 let dev_type = PcieDevicePortType::from(pcie_capabilities.device_type());
180 let slot = match dev_type {
181 PcieDevicePortType::PCIEEP
182 | PcieDevicePortType::LPCIEEP
183 | PcieDevicePortType::RCIEP
184 | PcieDevicePortType::RCEC => String::from(""),
185 _ => format!(" (Slot{})", is_set(pcie_capabilities.slot_implemented())),
186 };
187 write!(
188 f,
189 "Express (v{}) {}{}, MSI {:#02x}",
190 pcie_capabilities.version(),
191 dev_type,
192 slot,
193 pcie_capabilities.irq_number()
194 )
195 }
196
197 fn vendor(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 write!(f, "Vendor Specific Information: Len={:#2x}", self.config[self.offset + 2])
199 }
200}
201
202fn msi_mms_to_value(mms: u16) -> u8 {
203 match mms {
204 0x000 => 1,
205 0x001 => 2,
206 0x010 => 4,
207 0x011 => 8,
208 0x100 => 16,
209 0x101 => 32,
210 _ => 0,
211 }
212}
213
214bitfield! {
215 struct MsiControl(u16);
216 enable, _: 0;
217 mms_capable, _: 3, 1;
218 mms_enabled, _: 6, 4;
219 can_be_64bit, _: 7;
220 pvm_capable, _: 8;
221 _reserved, _: 15, 9;
222}
223
224#[derive(IntoBytes, KnownLayout, FromBytes, Immutable)]
225#[repr(C, packed)]
226struct Msi32Capability {
227 id: u8,
228 next: u8,
229 control: u16,
230 address: u32,
231 data: u16,
232}
233
234#[derive(IntoBytes, KnownLayout, FromBytes, Immutable)]
235#[repr(C, packed)]
236struct Msi64Capability {
237 id: u8,
238 next: u8,
239 control: u16,
240 address: u32,
241 address_upper: u32,
242 data: u16,
243}
244
245#[derive(IntoBytes, KnownLayout, FromBytes, Immutable)]
246#[repr(C, packed)]
247struct MsixCapability {
248 id: u8,
249 next: u8,
250 control: u16,
251 table: u32,
252 pba: u32,
253}
254
255bitfield! {
256 pub struct MsixControl(u16);
257 table_size, _: 10, 0;
258 _reserved, _: 13, 11;
259 function_mask, _: 14;
260 enable, _: 15;
261}
262
263bitfield! {
264 pub struct MsixBarField(u32);
265 bir, _: 2, 0;
266 offset, _: 31, 3;
267}
268
269bitfield! {
270 pub struct PcieCapabilitiesField(u16);
271 version, _: 3, 0;
272 device_type, _: 7, 4;
273 slot_implemented, _: 8;
274 irq_number, _: 13, 9;
275 _reserved, _: 15, 14;
276}
277
278enum PcieDevicePortType {
280 PCIEEP,
281 LPCIEEP,
282 RCIEP,
283 RCEC,
284 RPPCIERC,
285 UPPCIES,
286 DPPCIES,
287 PCIE2PCIB,
288 PCI2PCIEB,
289 Unknown(u16),
290}
291
292impl From<u16> for PcieDevicePortType {
293 fn from(value: u16) -> Self {
294 match value {
295 0b0000 => PcieDevicePortType::PCIEEP,
296 0b0001 => PcieDevicePortType::LPCIEEP,
297 0b1001 => PcieDevicePortType::RCIEP,
298 0b1010 => PcieDevicePortType::RCEC,
299 0b0100 => PcieDevicePortType::RPPCIERC,
300 0b0101 => PcieDevicePortType::UPPCIES,
301 0b0110 => PcieDevicePortType::DPPCIES,
302 0b0111 => PcieDevicePortType::PCIE2PCIB,
303 0b1000 => PcieDevicePortType::PCI2PCIEB,
304 _ => PcieDevicePortType::Unknown(value),
305 }
306 }
307}
308
309impl fmt::Display for PcieDevicePortType {
310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311 write!(
312 f,
313 "{}",
314 match self {
315 PcieDevicePortType::PCIEEP => "PCI Express Endpoint",
316 PcieDevicePortType::LPCIEEP => "Legacy PCI Express Endpoint",
317 PcieDevicePortType::RCIEP => "Root Complex Integrated Endpoint",
318 PcieDevicePortType::RCEC => "Root Complex Event Collector",
319 PcieDevicePortType::RPPCIERC => "Root Port of PCI Express Root Complex",
320 PcieDevicePortType::UPPCIES => "Upstream Port of PCI Express Switch",
321 PcieDevicePortType::DPPCIES => "Downstream Port of PCI Express Switch",
322 PcieDevicePortType::PCIE2PCIB => "PCI Express to PCI/PCI-X Bridge",
323 PcieDevicePortType::PCI2PCIEB => "PCI/PCI-X to PCI Express Bridge",
324 PcieDevicePortType::Unknown(x) => return write!(f, "Unknown PCIe Type ({:#x})", x),
325 }
326 )
327 }
328}
329
330#[derive(IntoBytes, KnownLayout, FromBytes, Immutable)]
331#[repr(C, packed)]
332struct PciExpressCapability {
333 id: u8,
334 next: u8,
335 pcie_capabilities: u16,
336 device_capabilities: u32,
337 device_control: u16,
338 device_status: u16,
339}
340
341enum ExtendedCapabilityType {
344 Null,
345 AdvancedErrorReporting,
346 VirtualChannelNoMfvc,
347 DeviceSerialNumber,
348 PowerBudgeting,
349 RootComplexLinkDeclaration,
350 RootComplexInternalLinkControl,
351 RootComplexEventCollectorEndpointAssociation,
352 MultiFunctionVirtualChannel,
353 VirtualChannel,
354 Rcrb,
355 Vendor,
356 Cac,
357 Acs,
358 Ari,
359 Ats,
360 SrIov,
361 MrIov,
362 Multicast,
363 Pri,
364 EnhancedAllocation,
365 ResizableBar,
366 DynamicPowerAllocation,
367 Tph,
368 LatencyToleranceReporting,
369 SecondaryPciExpress,
370 Pmux,
371 Pasid,
372 Lnr,
373 Dpc,
374 L1pmSubstates,
375 PrecisionTimeMeasurement,
376 Mpcie,
377 FrsQueueing,
378 ReadinessTimeReporting,
379 DesignatedVendor,
380 VfResizableBar,
381 DataLinkFeature,
382 PhysicalLayer16,
383 LaneMarginingAtReceiver,
384 HierarchyId,
385 NativePcieEnclosure,
386 PhysicalLayer32,
387 AlternateProtocol,
388 SystemFirmwareIntermediary,
389 Unknown(u16),
390}
391
392impl From<u16> for ExtendedCapabilityType {
393 fn from(value: u16) -> Self {
394 match value {
395 0x00 => ExtendedCapabilityType::Null,
396 0x01 => ExtendedCapabilityType::AdvancedErrorReporting,
397 0x02 => ExtendedCapabilityType::VirtualChannelNoMfvc,
398 0x03 => ExtendedCapabilityType::DeviceSerialNumber,
399 0x04 => ExtendedCapabilityType::PowerBudgeting,
400 0x05 => ExtendedCapabilityType::RootComplexLinkDeclaration,
401 0x06 => ExtendedCapabilityType::RootComplexInternalLinkControl,
402 0x07 => ExtendedCapabilityType::RootComplexEventCollectorEndpointAssociation,
403 0x08 => ExtendedCapabilityType::MultiFunctionVirtualChannel,
404 0x09 => ExtendedCapabilityType::VirtualChannel,
405 0x0a => ExtendedCapabilityType::Rcrb,
406 0x0b => ExtendedCapabilityType::Vendor,
407 0x0c => ExtendedCapabilityType::Cac,
408 0x0d => ExtendedCapabilityType::Acs,
409 0x0e => ExtendedCapabilityType::Ari,
410 0x0f => ExtendedCapabilityType::Ats,
411 0x10 => ExtendedCapabilityType::SrIov,
412 0x11 => ExtendedCapabilityType::MrIov,
413 0x12 => ExtendedCapabilityType::Multicast,
414 0x13 => ExtendedCapabilityType::Pri,
415 0x14 => ExtendedCapabilityType::EnhancedAllocation,
416 0x15 => ExtendedCapabilityType::ResizableBar,
417 0x16 => ExtendedCapabilityType::DynamicPowerAllocation,
418 0x17 => ExtendedCapabilityType::Tph,
419 0x18 => ExtendedCapabilityType::LatencyToleranceReporting,
420 0x19 => ExtendedCapabilityType::SecondaryPciExpress,
421 0x1a => ExtendedCapabilityType::Pmux,
422 0x1b => ExtendedCapabilityType::Pasid,
423 0x1c => ExtendedCapabilityType::Lnr,
424 0x1d => ExtendedCapabilityType::Dpc,
425 0x1e => ExtendedCapabilityType::L1pmSubstates,
426 0x1f => ExtendedCapabilityType::PrecisionTimeMeasurement,
427 0x20 => ExtendedCapabilityType::Mpcie,
428 0x21 => ExtendedCapabilityType::FrsQueueing,
429 0x22 => ExtendedCapabilityType::ReadinessTimeReporting,
430 0x23 => ExtendedCapabilityType::DesignatedVendor,
431 0x24 => ExtendedCapabilityType::VfResizableBar,
432 0x25 => ExtendedCapabilityType::DataLinkFeature,
433 0x26 => ExtendedCapabilityType::PhysicalLayer16,
434 0x27 => ExtendedCapabilityType::LaneMarginingAtReceiver,
435 0x28 => ExtendedCapabilityType::HierarchyId,
436 0x29 => ExtendedCapabilityType::NativePcieEnclosure,
437 0x2a => ExtendedCapabilityType::PhysicalLayer32,
438 0x2b => ExtendedCapabilityType::AlternateProtocol,
439 0x2c => ExtendedCapabilityType::SystemFirmwareIntermediary,
440 _ => ExtendedCapabilityType::Unknown(value),
441 }
442 }
443}
444
445pub struct ExtendedCapability {
446 offset: usize,
447 cap_type: ExtendedCapabilityType,
448}
449
450impl ExtendedCapability {
451 pub fn new(capability: &FidlExtCapability) -> Self {
452 ExtendedCapability {
453 offset: capability.offset as usize,
454 cap_type: ExtendedCapabilityType::from(capability.id),
455 }
456 }
457}
458
459impl fmt::Display for ExtendedCapability {
460 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461 write!(f, "Capabilities: [0x{:03x}] ", self.offset)?;
462 match self.cap_type {
463 ExtendedCapabilityType::Null => write!(f, "Null"),
464 ExtendedCapabilityType::AdvancedErrorReporting => write!(f, "Advanced Error Reporting"),
465 ExtendedCapabilityType::VirtualChannelNoMfvc => write!(f, "Virtual Channel (no MFVC)"),
466 ExtendedCapabilityType::DeviceSerialNumber => write!(f, "Device Serial Number"),
467 ExtendedCapabilityType::PowerBudgeting => write!(f, "Power Budgeting"),
468 ExtendedCapabilityType::RootComplexLinkDeclaration => {
469 write!(f, "Root Complex Link Declaration")
470 }
471 ExtendedCapabilityType::RootComplexInternalLinkControl => {
472 write!(f, "Root Complex Internal Link Control")
473 }
474 ExtendedCapabilityType::RootComplexEventCollectorEndpointAssociation => {
475 write!(f, "Root Complex Event Collector Endpoint Association")
476 }
477 ExtendedCapabilityType::MultiFunctionVirtualChannel => {
478 write!(f, "Multi-Function Virtual Channel")
479 }
480 ExtendedCapabilityType::VirtualChannel => write!(f, "Virtual Channel"),
481 ExtendedCapabilityType::Rcrb => write!(f, "RCRB"),
482 ExtendedCapabilityType::Vendor => write!(f, "Vendor Specific Option"),
483 ExtendedCapabilityType::Cac => write!(f, "CAC"),
484 ExtendedCapabilityType::Acs => write!(f, "Access Control Services"),
485 ExtendedCapabilityType::Ari => write!(f, "Alternative Routing-ID Interpretation (ARI)"),
486 ExtendedCapabilityType::Ats => write!(f, "Address Translation Services (ATS)"),
487 ExtendedCapabilityType::SrIov => write!(f, "Single Root I/O Virtualization (SR-IOV)"),
488 ExtendedCapabilityType::MrIov => write!(f, "Multi-Root I/O Virtualization (MR-IOV)"),
489 ExtendedCapabilityType::Multicast => write!(f, "Multicast"),
490 ExtendedCapabilityType::Pri => write!(f, "Page Request Interface (PRI)"),
491 ExtendedCapabilityType::EnhancedAllocation => write!(f, "Enhanced Allocation"),
492 ExtendedCapabilityType::ResizableBar => write!(f, "Resizable BAR"),
493 ExtendedCapabilityType::DynamicPowerAllocation => write!(f, "Dynamic Power Allocation"),
494 ExtendedCapabilityType::Tph => write!(f, "TLP Processing Hints (TPH)"),
495 ExtendedCapabilityType::LatencyToleranceReporting => {
496 write!(f, "Latency Tolerance Reporting")
497 }
498 ExtendedCapabilityType::SecondaryPciExpress => write!(f, "Secondary PCI Express"),
499 ExtendedCapabilityType::Pmux => write!(f, "Protocol Multiplexing (PMUX)"),
500 ExtendedCapabilityType::Pasid => write!(f, "Process Address Space ID (PASID)"),
501 ExtendedCapabilityType::Lnr => write!(f, "LN Requester (LNR)"),
502 ExtendedCapabilityType::Dpc => write!(f, "Downstream Port Containment (DPC)"),
503 ExtendedCapabilityType::L1pmSubstates => write!(f, "L1 PM Substates"),
504 ExtendedCapabilityType::PrecisionTimeMeasurement => {
505 write!(f, "Precision Time Measurement (PTM)")
506 }
507 ExtendedCapabilityType::Mpcie => write!(f, "M-PCIe"),
508 ExtendedCapabilityType::FrsQueueing => write!(f, "FRS Queueing"),
509 ExtendedCapabilityType::ReadinessTimeReporting => write!(f, "Readiness Time Reporting"),
510 ExtendedCapabilityType::DesignatedVendor => write!(f, "Designated Vendor-Specific"),
511 ExtendedCapabilityType::VfResizableBar => write!(f, "VF Resizable BAR"),
512 ExtendedCapabilityType::DataLinkFeature => write!(f, "Data Link Feature"),
513 ExtendedCapabilityType::PhysicalLayer16 => write!(f, "Physical Layer 16.0 GT/s"),
514 ExtendedCapabilityType::LaneMarginingAtReceiver => {
515 write!(f, "Lane Margining at Receiver")
516 }
517 ExtendedCapabilityType::HierarchyId => write!(f, "Hierarchy ID"),
518 ExtendedCapabilityType::NativePcieEnclosure => write!(f, "Native PCIe Enclosure"),
519 ExtendedCapabilityType::PhysicalLayer32 => write!(f, "Physical Layer 32.0 GT/s"),
520 ExtendedCapabilityType::AlternateProtocol => write!(f, "Alternate Protocol"),
521 ExtendedCapabilityType::SystemFirmwareIntermediary => {
522 write!(f, "System Firmware Intermediary")
523 }
524 ExtendedCapabilityType::Unknown(id) => {
525 write!(f, "Unknown Extended Capability (id = {:#04x})", id)
526 }
527 }
528 }
529}
530
531#[cfg(test)]
532mod tests {
533 use super::*;
534
535 #[test]
536 fn test_extended_capability_display() {
537 let cap = FidlExtCapability { id: 0x0001, offset: 0x0100 };
538 let ext_cap = ExtendedCapability::new(&cap);
539 assert_eq!(format!("{}", ext_cap), "Capabilities: [0x100] Advanced Error Reporting");
540
541 let cap_zero = FidlExtCapability { id: 0x0001, offset: 0x0 };
542 let ext_cap_zero = ExtendedCapability::new(&cap_zero);
543 assert_eq!(format!("{}", ext_cap_zero), "Capabilities: [0x000] Advanced Error Reporting");
544
545 let cap_unknown = FidlExtCapability { id: 0xabcd, offset: 0x0200 };
546 let ext_cap_unknown = ExtendedCapability::new(&cap_unknown);
547 assert_eq!(
548 format!("{}", ext_cap_unknown),
549 "Capabilities: [0x200] Unknown Extended Capability (id = 0xabcd)"
550 );
551 }
552}