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 Common(cdata.Struct):
11    _pack_ = 1
12    _fields_ = [
13        ('vendor_id', ctypes.c_uint16),
14        ('device_id', ctypes.c_uint16),
15        ('command', ctypes.c_uint16),
16        ('status', ctypes.c_uint16),
17        ('revision_id', ctypes.c_uint32, 8),
18        ('class_code', ctypes.c_uint32, 24),
19        ('cacheline_size', ctypes.c_uint8),
20        ('latency_timer', ctypes.c_uint8),
21        ('header_type', ctypes.c_uint8, 7),
22        ('multi_function', ctypes.c_uint8, 1),
23        ('bist', ctypes.c_uint8),
24    ]
25
26class MemoryBar32(cdata.Struct):
27    _pack_ = 1
28    _fields_ = [
29        ('indicator', ctypes.c_uint8, 1),
30        ('type', ctypes.c_uint8, 2),
31        ('prefetchable', ctypes.c_uint8, 1),
32        ('base_z', ctypes.c_uint32, 28),
33    ]
34
35    resource_type = "memory"
36
37    @property
38    def base(self):
39        return self.base_z << 4
40
41class MemoryBar64(cdata.Struct):
42    _pack_ = 1
43    _fields_ = [
44        ('indicator', ctypes.c_uint8, 1),
45        ('type', ctypes.c_uint8, 2),
46        ('prefetchable', ctypes.c_uint8, 1),
47        ('base_z', ctypes.c_uint64, 60),
48    ]
49
50    resource_type = "memory"
51
52    @property
53    def base(self):
54        return self.base_z << 4
55
56class IOBar(cdata.Struct):
57    _pack_ = 1
58    _fields_ = [
59        ('indicator', ctypes.c_uint8, 1),
60        ('reserved', ctypes.c_uint8, 1),
61        ('base_z', ctypes.c_uint32, 30),
62    ]
63
64    resource_type = "io_port"
65
66    @property
67    def base(self):
68        return self.base_z << 2
69
70PCIE_BAR_SPACE_MASK = 0x1
71PCIE_BAR_MEMORY_SPACE = 0x0
72PCIE_BAR_IO_SPACE = 0x1
73
74PCIE_BAR_TYPE_MASK = 0x6
75PCIE_BAR_TYPE_32_BIT = 0x0
76PCIE_BAR_TYPE_64_BIT = 0x4
77
78def header_type_0_field_list(addr):
79    bar_list = list()
80    bar_addr = addr
81    bar_end = addr + 0x18
82    while bar_addr < bar_end:
83        bar = ctypes.c_uint32.from_address(bar_addr).value
84        idx = int((bar_addr - addr) / 4)
85        if (bar & PCIE_BAR_SPACE_MASK) == PCIE_BAR_MEMORY_SPACE:
86            if (bar & PCIE_BAR_TYPE_MASK) == PCIE_BAR_TYPE_64_BIT:
87                bar_list.append((f"bar{idx}", MemoryBar64))
88                bar_addr += 0x8
89            else:
90                bar_list.append((f"bar{idx}", MemoryBar32))
91                bar_addr += 0x4
92        else:
93            bar_list.append((f"bar{idx}", IOBar))
94            bar_addr += 0x4
95
96    class Bars(cdata.Struct):
97        _pack_ = 1
98        _fields_ = bar_list
99
100        def __iter__(self):
101            for f in self._fields_:
102                yield getattr(self, f[0])
103
104    return [
105        ('bars', Bars),
106        ('cardbus_cis_pointer', ctypes.c_uint32),
107        ('subsystem_vendor_id', ctypes.c_uint16),
108        ('subsystem_device_id', ctypes.c_uint16),
109        ('expansion_rom_base_address', ctypes.c_uint32),
110        ('capability_pointer', ctypes.c_uint8),
111        ('reserved', ctypes.c_uint8 * 7),
112        ('interrupt_line', ctypes.c_uint8),
113        ('interrupt_pin', ctypes.c_uint8),
114        ('min_gnt', ctypes.c_uint8),
115        ('max_lat', ctypes.c_uint8),
116    ]
117
118def header_type_1_field_list(addr):
119    bar_list = list()
120    bar_addr = addr
121    bar_end = addr + 0x08
122    while bar_addr < bar_end:
123        bar = ctypes.c_uint32.from_address(addr).value
124        idx = int((bar_addr - addr) / 4)
125        if (bar & PCIE_BAR_SPACE_MASK) == PCIE_BAR_MEMORY_SPACE:
126            if (bar & PCIE_BAR_TYPE_MASK) == PCIE_BAR_TYPE_64_BIT:
127                bar_list.append((f"bar{idx}", MemoryBar64))
128                bar_addr += 0x8
129            else:
130                bar_list.append((f"bar{idx}", MemoryBar32))
131                bar_addr += 0x4
132        else:
133            bar_list.append((f"bar{idx}", IOBar))
134            bar_addr += 0x4
135
136    class Bars(cdata.Struct):
137        _pack_ = 1
138        _fields_ = bar_list
139
140        def __iter__(self):
141            for f in self._fields_:
142                yield getattr(self, f[0])
143
144    return [
145        ('bars', Bars),
146        ('primary_bus_number', ctypes.c_uint8),
147        ('secondary_bus_number', ctypes.c_uint8),
148        ('subordinate_bus_number', ctypes.c_uint8),
149        ('secondary_latency_timer', ctypes.c_uint8),
150        ('io_base', ctypes.c_uint8),
151        ('io_limit', ctypes.c_uint8),
152        ('secondary_status', ctypes.c_uint16),
153        ('memory_base', ctypes.c_uint16),
154        ('memory_limit', ctypes.c_uint16),
155        ('prefetchable_memory_base', ctypes.c_uint16),
156        ('prefetchable_memory_limit', ctypes.c_uint16),
157        ('prefetchable_base_upper_32_bits', ctypes.c_uint32),
158        ('prefetchable_limit_upper_32_bits', ctypes.c_uint32),
159        ('io_base_upper_16_bits', ctypes.c_uint16),
160        ('io_limit_upper_16_bits', ctypes.c_uint16),
161        ('capability_pointer', ctypes.c_uint8),
162        ('reserved', ctypes.c_uint8 * 3),
163        ('expansion_rom_base_address', ctypes.c_uint32),
164        ('interrupt_line', ctypes.c_uint8),
165        ('interrupt_pin', ctypes.c_uint8),
166        ('bridge_control', ctypes.c_uint16),
167    ]
168
169def header_field_list(addr):
170    common_header = Common.from_address(addr)
171    if common_header.header_type == 0x00:
172        return header_type_0_field_list(addr + ctypes.sizeof(Common))
173    elif common_header.header_type == 0x01:
174        return header_type_1_field_list(addr + ctypes.sizeof(Common))
175    else:
176        return [('unparsed_data', ctypes.c_uint8 * 0x30)]
177
178def header_factory(field_list):
179    class Header(cdata.Struct):
180        _pack_ = 1
181        _fields_ = copy.copy(Common._fields_) + field_list
182    return Header
183
184def header(data):
185    """Create class based on decode of a PCI configuration space header from raw data."""
186    buf = ctypes.create_string_buffer(data, len(data))
187    addr = ctypes.addressof(buf)
188    field_list = header_field_list(addr)
189    return header_factory(field_list).from_buffer_copy(data)
190