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
9
10class Capability:
11    # Capability names from PCI Local Bus Specification and PCI Express Base Specification
12    _cap_names_ = {
13        0x01: "Power Management",
14        0x02: "AGP",
15        0x03: "VPD",
16        0x04: "Slot Identification",
17        0x05: "MSI",
18        0x06: "CompactPCI Hot Swap",
19        0x07: "PCI-X",
20        0x08: "Hyper Transport",
21        0x09: "Vendor-Specific",
22        0x0a: "Debug port",
23        0x0b: "CompactPCI Central Resource Control",
24        0x0c: "Hot Plug",
25        0x0d: "Subsystem ID and Subsystem Vendor ID",
26        0x0e: "AGP 8x",
27        0x0f: "Secure Device",
28        0x10: "PCI Express",
29        0x11: "MSI-X",
30        0x13: "Conventional PCI Advanced Features",
31        0x14: "Enhanced Allocation",
32        0x15: "FPB",
33    }
34
35    @property
36    def name(self):
37        if self.id in self._cap_names_.keys():
38            return self._cap_names_[self.id]
39        else:
40            return f"Reserved ({hex(self.id)})"
41
42class CapabilityListRegister(cdata.Struct, Capability):
43    _pack_ = 1
44    _fields_ = [
45        ('id', ctypes.c_uint8),
46        ('next_cap_ptr', ctypes.c_uint8),
47    ]
48
49# Power Management (0x01)
50
51class PowerManagement(cdata.Struct, Capability):
52    _pack_ = 1
53    _fields_ = copy.copy(CapabilityListRegister._fields_) + [
54        ('version', ctypes.c_uint16, 3),
55        ('pme_clock', ctypes.c_uint16, 1),
56        ('immediate_readiness_on_return_to_d0', ctypes.c_uint16, 1),
57        ('device_specific_initialization', ctypes.c_uint16, 1),
58        ('aux_current', ctypes.c_uint16, 3),
59        ('d1_support', ctypes.c_uint16, 1),
60        ('d2_support', ctypes.c_uint16, 1),
61        ('pme_support', ctypes.c_uint16, 5),
62        ('power_state', ctypes.c_uint16, 2),
63        ('reserved1', ctypes.c_uint16, 1),
64        ('no_soft_reset', ctypes.c_uint16, 1),
65        ('reserved2', ctypes.c_uint16, 4),
66        ('pme_en', ctypes.c_uint16, 1),
67        ('data_select', ctypes.c_uint16, 4),
68        ('data_scale', ctypes.c_uint16, 2),
69        ('pme_status', ctypes.c_uint16, 1),
70        ('reserved3', ctypes.c_uint8, 6),
71        ('undefined', ctypes.c_uint8, 2),
72        ('data', ctypes.c_uint8),
73    ]
74
75def parse_power_management(buf, cap_ptr):
76    return PowerManagement.from_buffer_copy(buf, cap_ptr)
77
78# MSI (0x05)
79
80def MSI_factory(field_list):
81    class MSI(cdata.Struct, Capability):
82        _pack_ = 1
83        _fields_ = copy.copy(CapabilityListRegister._fields_) + [
84            ('msi_enable', ctypes.c_uint16, 1),
85            ('multiple_message_capable', ctypes.c_uint16, 3),
86            ('multiple_message_enable', ctypes.c_uint16, 3),
87            ('address_64bit', ctypes.c_uint16, 1),
88            ('per_vector_masking_capable', ctypes.c_uint16, 1),
89            ('reserved', ctypes.c_uint16, 7),
90        ] + field_list
91
92    return MSI
93
94def msi_field_list(addr):
95    field_list = list()
96    msgctrl = MSI_factory([]).from_address(addr)
97
98    if msgctrl.address_64bit == 1:
99        field_list.append(('message_address', ctypes.c_uint64))
100    else:
101        field_list.append(('message_address', ctypes.c_uint32))
102
103    field_list.append(('message_data', ctypes.c_uint16))
104
105    if msgctrl.per_vector_masking_capable:
106        field_list.append(('reserved', ctypes.c_uint16))
107        field_list.append(('mask_bits', ctypes.c_uint32))
108        field_list.append(('pending_bits', ctypes.c_uint32))
109
110    return field_list
111
112def parse_msi(buf, cap_ptr):
113    addr = ctypes.addressof(buf) + cap_ptr
114    field_list = msi_field_list(addr)
115    return MSI_factory(field_list).from_buffer_copy(buf, cap_ptr)
116
117# MSI-X (0x11)
118
119class MSIX(cdata.Struct, Capability):
120    _pack_ = 1
121    _fields_ = copy.copy(CapabilityListRegister._fields_) + [
122        ('table_size_z', ctypes.c_uint16, 10),
123        ('reserved', ctypes.c_uint16, 3),
124        ('function_mask', ctypes.c_uint16, 1),
125        ('msix_enable', ctypes.c_uint16, 1),
126        ('table_bir', ctypes.c_uint32, 3),
127        ('table_offset_z', ctypes.c_uint32, 29),
128        ('pba_bir', ctypes.c_uint32, 3),
129        ('pba_offset_z', ctypes.c_uint32, 29),
130    ]
131
132    @property
133    def table_size(self):
134        return self.table_size_z + 1
135
136    @property
137    def table_offset(self):
138        return self.table_offset_z << 3
139
140    @property
141    def pba_offset(self):
142        return self.pba_offset_z << 3
143
144def parse_msix(buf, cap_ptr):
145    return MSIX.from_buffer_copy(buf, cap_ptr)
146
147# Module API
148
149capability_parsers = {
150    0x1: parse_power_management,
151    0x5: parse_msi,
152    0x11: parse_msix,
153}
154
155def capabilities(data, cap_ptr):
156    buf = ctypes.create_string_buffer(data, len(data))
157
158    acc = list()
159    while cap_ptr != 0:
160        caplist = CapabilityListRegister.from_buffer_copy(buf, cap_ptr)
161        if caplist.id in capability_parsers.keys():
162            acc.append(capability_parsers[caplist.id](buf, cap_ptr))
163        else:
164            acc.append(caplist)
165        cap_ptr = caplist.next_cap_ptr
166
167    return acc
168