1# Copyright (C) 2021-2022 Intel Corporation.
2#
3# SPDX-License-Identifier: BSD-3-Clause
4#
5
6import ctypes
7import copy
8import inspectorlib.cdata as cdata
9from .header import MemoryBar32, MemoryBar64, IOBar, \
10    PCIE_BAR_SPACE_MASK, PCIE_BAR_MEMORY_SPACE, PCIE_BAR_IO_SPACE, \
11    PCIE_BAR_TYPE_MASK, PCIE_BAR_TYPE_32_BIT, PCIE_BAR_TYPE_64_BIT
12
13class ExtendedCapability:
14    # Capability names from PCI Express Base Specification, mostly Table 9-23
15    _cap_names_ = {
16        0x01: "Advanced Error Reporting",
17        0x02: "Virtual Channel",
18        0x03: "Device Serial Number",
19        0x04: "Power Budgeting",
20        0x05: "Root Complex Link Declaration",
21        0x06: "Root Complex Internal Link Control",
22        0x07: "Root Complex Event Collector Endpoint Association",
23        0x08: "Multi-Function Virtual Channel",
24        0x09: "Virtual Channel",
25        0x0a: "RCRB Header",
26        0x0b: "Vendor-Specific Extended",
27        0x0c: "Configuration Access Correlation",
28        0x0d: "ACS",
29        0x0e: "ARI",
30        0x0f: "ATS",
31        0x10: "SR-IOV",
32        0x11: "MR-IOV",
33        0x12: "Multicast",
34        0x13: "PRI",
35        0x15: "Resizable BAR",
36        0x16: "DPA",
37        0x17: "TPH Requester",
38        0x18: "LTR",
39        0x19: "Secondary PCI Express",
40        0x1a: "PMUX",
41        0x1b: "PASID",
42        0x1c: "LNR",
43        0x1d: "DPC",
44        0x1e: "L1 PM Substates",
45        0x1f: "PTM",
46        0x20: "M-PCIe",
47        0x21: "FRS Queueing",
48        0x22: "Readiness Time Reporting",
49        0x23: "Designated Vendor-Specific",
50        0x24: "VF Resizable BAR",
51        0x25: "Data Link Feature",
52        0x26: "Physical Layer 16.0 GT/s",
53        0x27: "Lane Margining at the Receiver",
54        0x28: "Hierarchy ID",
55        0x29: "NPEM",
56        0x2a: "Physical Layer 32.0 GT/s",
57        0x2b: "Alternate Protocol",
58        0x2c: "SFI",
59    }
60
61    @property
62    def name(self):
63        if self.id in self._cap_names_.keys():
64            return self._cap_names_[self.id]
65        else:
66            return f"Reserved Extended ({hex(self.id)})"
67
68    @property
69    def next_cap_ptr(self):
70        # Classes inherit ExtendedCapability must implement the attribute next_cap_ptr_raw
71        return self.next_cap_ptr_raw & 0xffc
72
73class ExtendedCapabilityListRegister(cdata.Struct, ExtendedCapability):
74    _pack_ = 1
75    _fields_ = [
76        ('id', ctypes.c_uint32, 16),
77        ('version', ctypes.c_uint32, 4),
78        ('next_cap_ptr_raw', ctypes.c_uint32, 12),
79    ]
80
81# SR-IOV (0x10)
82
83class SRIOVBase(cdata.Struct, ExtendedCapability):
84    _pack_ = 1
85    _fields_ = copy.copy(ExtendedCapabilityListRegister._fields_) + [
86        # SR-IOV Capabilities Register
87        ('vf_migration_capable', ctypes.c_uint32, 1),
88        ('ari_capable_hierarchy_preserved', ctypes.c_uint32, 1),
89        ('vf_10_bit_tag_requester_supported', ctypes.c_uint32, 1),
90        ('reserved1', ctypes.c_uint32, 18),
91        ('vf_migration_interrupt_message_number', ctypes.c_uint32, 11),
92
93        # SR-IOV Control Register
94        ('vf_enable', ctypes.c_uint32, 1),
95        ('vf_migration_enable', ctypes.c_uint32, 1),
96        ('vf_migration_interrupt_enable', ctypes.c_uint32, 1),
97        ('vf_mse', ctypes.c_uint32, 1),
98        ('ari_capable_hierarchy', ctypes.c_uint32, 1),
99        ('vf_10_bit_tag_requester_enable', ctypes.c_uint32, 1),
100        ('reserved2', ctypes.c_uint32, 10),
101
102        # SR-IOV Status Register
103        ('vf_migration_status', ctypes.c_uint32, 1),
104        ('reserved3', ctypes.c_uint32, 15),
105
106        ('initial_vfs', ctypes.c_uint16),
107        ('total_vfs', ctypes.c_uint16),
108        ('num_vfs', ctypes.c_uint16),
109        ('function_dependency_link', ctypes.c_uint8),
110        ('reserved4', ctypes.c_uint8),
111        ('first_vf_offset', ctypes.c_uint16),
112        ('vf_stride', ctypes.c_uint16),
113        ('reserved5', ctypes.c_uint16),
114        ('vf_device_id', ctypes.c_uint16),
115
116        ('supported_page_sizes', ctypes.c_uint32),
117        ('system_page_size', ctypes.c_uint32),
118    ]
119
120def SRIOV_factory(addr):
121    vf_bars_list = list()
122    bar_base = addr + ctypes.sizeof(SRIOVBase)
123    bar_addr = bar_base
124    bar_end = bar_base + 0x18
125    while bar_addr < bar_end:
126        bar = ctypes.c_uint32.from_address(bar_addr).value
127        idx = int((bar_addr - bar_base) / 4)
128        if (bar & PCIE_BAR_SPACE_MASK) == PCIE_BAR_MEMORY_SPACE:
129            if (bar & PCIE_BAR_TYPE_MASK) == PCIE_BAR_TYPE_64_BIT:
130                vf_bars_list.append((f"vf_bar{idx}", MemoryBar64))
131                bar_addr += 0x8
132            else:
133                vf_bars_list.append((f"vf_bar{idx}", MemoryBar32))
134                bar_addr += 0x4
135        else:
136            vf_bars_list.append((f"vf_bar{idx}", IOBar))
137            bar_addr += 0x4
138
139    class SRIOV(cdata.Struct, ExtendedCapability):
140        class VFBars(cdata.Struct):
141            _pack_ = 1
142            _fields_ = vf_bars_list
143
144            def __iter__(self):
145                for f in self._fields_:
146                    yield getattr(self, f[0])
147
148        _pack_ = 1
149        _fields_ = copy.copy(SRIOVBase._fields_) + [
150            ('vf_bars', VFBars),
151            ('vf_migration_state_array_offset', ctypes.c_uint32),
152        ]
153
154    return SRIOV
155
156def parse_sriov(buf, cap_ptr):
157    return SRIOV_factory(ctypes.addressof(buf) + cap_ptr).from_buffer_copy(buf, cap_ptr)
158
159# Module API
160
161capability_parsers = {
162    0x10: parse_sriov,
163}
164
165def extended_capabilities(data):
166    buf = ctypes.create_string_buffer(data, len(data))
167    cap_ptr = 0x100
168
169    acc = list()
170    while cap_ptr != 0:
171        caplist = ExtendedCapabilityListRegister.from_buffer_copy(buf, cap_ptr)
172        if caplist.id in capability_parsers.keys():
173            acc.append(capability_parsers[caplist.id](buf, cap_ptr))
174        elif caplist.id != 0:
175            acc.append(caplist)
176        cap_ptr = caplist.next_cap_ptr
177
178    return acc
179