Skip to main content

lspci/
capability.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4use 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
12// Capability types are documented in PCI Local Bus Specification v3.0 Appendix H
13enum 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        // MSI: Enable+ Count=1/1 Maskable- 64bit+
117        // Address: 00000000fee00698  Data: 0000
118        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        // PCIe Base Specification v4 section 7.5.3
178        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
278// PCIe Base Specification Table 7-17: PCI Express Capabilities Register
279enum 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
341// PCIe Extended Capability IDs.
342// PCIe Base Specification rev4, chapter 7.6.
343enum 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}