acrn-hypervisor/misc/config_tools/board_inspector/pcieparser/header.py
Junjie Mao 3a395bb342 board_inspector/pcieparser: add PCIe config space parser
This patch adds a parser of PCI-compatible configuration space read from
sysfs. The headers and capability lists are fully parsed, but only a couple
of capabilities are parsed completely. Parsing of additional capabilities
will be added on an on-demand basis.

v1 -> v2:
 - Fix a typo that causes incorrect parsing of BAR types
 - Parse capability structures using from_buffer_copy instead of
   from_address

Tracked-On: #5922
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
2021-05-16 19:02:00 +08:00

190 lines
5.7 KiB
Python

# Copyright (C) 2021 Intel Corporation. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
import ctypes
import copy
import lib.cdata as cdata
class Common(cdata.Struct):
_pack_ = 1
_fields_ = [
('vendor_id', ctypes.c_uint16),
('device_id', ctypes.c_uint16),
('command', ctypes.c_uint16),
('status', ctypes.c_uint16),
('revision_id', ctypes.c_uint32, 8),
('class_code', ctypes.c_uint32, 24),
('cacheline_size', ctypes.c_uint8),
('latency_timer', ctypes.c_uint8),
('header_type', ctypes.c_uint8, 7),
('multi_function', ctypes.c_uint8, 1),
('bist', ctypes.c_uint8),
]
class MemoryBar32(cdata.Struct):
_pack_ = 1
_fields_ = [
('indicator', ctypes.c_uint8, 1),
('type', ctypes.c_uint8, 2),
('prefetchable', ctypes.c_uint8, 1),
('base_z', ctypes.c_uint32, 28),
]
resource_type = "memory"
@property
def base(self):
return self.base_z << 4
class MemoryBar64(cdata.Struct):
_pack_ = 1
_fields_ = [
('indicator', ctypes.c_uint8, 1),
('type', ctypes.c_uint8, 2),
('prefetchable', ctypes.c_uint8, 1),
('base_z', ctypes.c_uint64, 60),
]
resource_type = "memory"
@property
def base(self):
return self.base_z << 4
class IOBar(cdata.Struct):
_pack_ = 1
_fields_ = [
('indicator', ctypes.c_uint8, 1),
('reserved', ctypes.c_uint8, 1),
('base_z', ctypes.c_uint32, 30),
]
resource_type = "io_port"
@property
def base(self):
return self.base_z << 2
PCIE_BAR_SPACE_MASK = 0x1
PCIE_BAR_MEMORY_SPACE = 0x0
PCIE_BAR_IO_SPACE = 0x1
PCIE_BAR_TYPE_MASK = 0x6
PCIE_BAR_TYPE_32_BIT = 0x0
PCIE_BAR_TYPE_64_BIT = 0x4
def header_type_0_field_list(addr):
bar_list = list()
bar_addr = addr
bar_end = addr + 0x18
while bar_addr < bar_end:
bar = ctypes.c_uint32.from_address(bar_addr).value
idx = int((bar_addr - addr) / 4)
if (bar & PCIE_BAR_SPACE_MASK) == PCIE_BAR_MEMORY_SPACE:
if (bar & PCIE_BAR_TYPE_MASK) == PCIE_BAR_TYPE_64_BIT:
bar_list.append((f"bar{idx}", MemoryBar64))
bar_addr += 0x8
else:
bar_list.append((f"bar{idx}", MemoryBar32))
bar_addr += 0x4
else:
bar_list.append((f"bar{idx}", IOBar))
bar_addr += 0x4
class Bars(cdata.Struct):
_pack_ = 1
_fields_ = bar_list
def __iter__(self):
for f in self._fields_:
yield getattr(self, f[0])
return [
('bars', Bars),
('cardbus_cis_pointer', ctypes.c_uint32),
('subsystem_vendor_id', ctypes.c_uint16),
('subsystem_device_id', ctypes.c_uint16),
('expansion_rom_base_address', ctypes.c_uint32),
('capability_pointer', ctypes.c_uint8),
('reserved', ctypes.c_uint8 * 7),
('interrupt_line', ctypes.c_uint8),
('interrupt_pin', ctypes.c_uint8),
('min_gnt', ctypes.c_uint8),
('max_lat', ctypes.c_uint8),
]
def header_type_1_field_list(addr):
bar_list = list()
bar_addr = addr
bar_end = addr + 0x08
while bar_addr < bar_end:
bar = ctypes.c_uint32.from_address(addr).value
idx = int((bar_addr - addr) / 4)
if (bar & PCIE_BAR_SPACE_MASK) == PCIE_BAR_MEMORY_SPACE:
if (bar & PCIE_BAR_TYPE_MASK) == PCIE_BAR_TYPE_64_BIT:
bar_list.append((f"bar{idx}", MemoryBar64))
bar_addr += 0x8
else:
bar_list.append((f"bar{idx}", MemoryBar32))
bar_addr += 0x4
else:
bar_list.append((f"bar{idx}", IOBar))
bar_addr += 0x4
class Bars(cdata.Struct):
_pack_ = 1
_fields_ = bar_list
def __iter__(self):
for f in self._fields_:
yield getattr(self, f[0])
return [
('bars', Bars),
('primary_bus_number', ctypes.c_uint8),
('secondary_bus_number', ctypes.c_uint8),
('subordinate_bus_number', ctypes.c_uint8),
('secondary_latency_timer', ctypes.c_uint8),
('io_base', ctypes.c_uint8),
('io_limit', ctypes.c_uint8),
('secondary_status', ctypes.c_uint16),
('memory_base', ctypes.c_uint16),
('memory_limit', ctypes.c_uint16),
('prefetchable_memory_base', ctypes.c_uint16),
('prefetchable_memory_limit', ctypes.c_uint16),
('prefetchable_base_upper_32_bits', ctypes.c_uint32),
('prefetchable_limit_upper_32_bits', ctypes.c_uint32),
('io_base_upper_16_bits', ctypes.c_uint16),
('io_limit_upper_16_bits', ctypes.c_uint16),
('capability_pointer', ctypes.c_uint8),
('reserved', ctypes.c_uint8 * 3),
('expansion_rom_base_address', ctypes.c_uint32),
('interrupt_line', ctypes.c_uint8),
('interrupt_pin', ctypes.c_uint8),
('bridge_control', ctypes.c_uint16),
]
def header_field_list(addr):
common_header = Common.from_address(addr)
if common_header.header_type == 0x00:
return header_type_0_field_list(addr + ctypes.sizeof(Common))
elif common_header.header_type == 0x01:
return header_type_1_field_list(addr + ctypes.sizeof(Common))
else:
return [('data', ctypes.c_uint8 * 0x30)]
def header_factory(field_list):
class Header(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(Common._fields_) + field_list
return Header
def header(data):
"""Create class based on decode of a PCI configuration space header from raw data."""
buf = ctypes.create_string_buffer(data, len(data))
addr = ctypes.addressof(buf)
field_list = header_field_list(addr)
return header_factory(field_list).from_buffer_copy(data)