mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-21 13:08:42 +00:00
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>
This commit is contained in:
parent
0215603812
commit
3a395bb342
42
misc/config_tools/board_inspector/pcieparser/__init__.py
Normal file
42
misc/config_tools/board_inspector/pcieparser/__init__.py
Normal file
@ -0,0 +1,42 @@
|
||||
# Copyright (C) 2021 Intel Corporation. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import os
|
||||
import ctypes
|
||||
from collections import namedtuple
|
||||
|
||||
from pcieparser.header import header
|
||||
from pcieparser.caps import capabilities
|
||||
from pcieparser.extcaps import extended_capabilities
|
||||
|
||||
class PCIConfigSpace(namedtuple("PCIConfigSpace", ["header", "caps", "extcaps"])):
|
||||
def __repr__(self):
|
||||
acc = str(self.header)
|
||||
for cap in self.caps:
|
||||
acc += "\n"
|
||||
acc += str(cap)
|
||||
for extcap in self.extcaps:
|
||||
acc += "\n"
|
||||
acc += str(extcap)
|
||||
return acc
|
||||
|
||||
def has_cap(self, cap_name):
|
||||
for cap in self.caps:
|
||||
if cap_name == cap.name:
|
||||
return True
|
||||
for cap in self.extcaps:
|
||||
if cap_name == cap.name:
|
||||
return True
|
||||
return False
|
||||
|
||||
def parse_config_space(path):
|
||||
data = open(os.path.join(path, "config"), mode='rb').read()
|
||||
hdr = header(data)
|
||||
caps = capabilities(data, hdr.capability_pointer)
|
||||
config_space = PCIConfigSpace(hdr, caps, [])
|
||||
if config_space.has_cap("PCI Express"):
|
||||
extcaps = extended_capabilities(data)
|
||||
config_space = PCIConfigSpace(hdr, caps, extcaps)
|
||||
return config_space
|
163
misc/config_tools/board_inspector/pcieparser/caps.py
Normal file
163
misc/config_tools/board_inspector/pcieparser/caps.py
Normal file
@ -0,0 +1,163 @@
|
||||
# Copyright (C) 2021 Intel Corporation. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import ctypes
|
||||
import copy
|
||||
import lib.cdata as cdata
|
||||
|
||||
class Capability:
|
||||
# Capability names from PCI Local Bus Specification and PCI Express Base Specification
|
||||
_cap_names_ = {
|
||||
0x01: "Power Management",
|
||||
0x02: "AGP",
|
||||
0x03: "VPD",
|
||||
0x04: "Slot Identification",
|
||||
0x05: "MSI",
|
||||
0x06: "CompactPCI Hot Swap",
|
||||
0x07: "PCI-X",
|
||||
0x08: "Hyper Transport",
|
||||
0x09: "Vendor-Specific",
|
||||
0x0a: "Debug port",
|
||||
0x0b: "CompactPCI Central Resource Control",
|
||||
0x0c: "Hot Plug",
|
||||
0x0d: "Subsystem ID and Subsystem Vendor ID",
|
||||
0x0e: "AGP 8x",
|
||||
0x0f: "Secure Device",
|
||||
0x10: "PCI Express",
|
||||
0x11: "MSI-X",
|
||||
0x13: "Conventional PCI Advanced Features",
|
||||
0x14: "Enhanced Allocation",
|
||||
0x15: "FPB",
|
||||
}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
if self.id in self._cap_names_.keys():
|
||||
return self._cap_names_[self.id]
|
||||
else:
|
||||
return f"Reserved ({hex(self.id)})"
|
||||
|
||||
class CapabilityListRegister(cdata.Struct, Capability):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('id', ctypes.c_uint8),
|
||||
('next_cap_ptr', ctypes.c_uint8),
|
||||
]
|
||||
|
||||
# Power Management (0x01)
|
||||
|
||||
class PowerManagement(cdata.Struct, Capability):
|
||||
_pack_ = 1
|
||||
_fields_ = copy.copy(CapabilityListRegister._fields_) + [
|
||||
('version', ctypes.c_uint16, 3),
|
||||
('pme_clock', ctypes.c_uint16, 1),
|
||||
('immediate_readiness_on_return_to_d0', ctypes.c_uint16, 1),
|
||||
('device_specific_initialization', ctypes.c_uint16, 1),
|
||||
('aux_current', ctypes.c_uint16, 3),
|
||||
('d1_support', ctypes.c_uint16, 1),
|
||||
('d2_support', ctypes.c_uint16, 1),
|
||||
('pme_support', ctypes.c_uint16, 5),
|
||||
('power_state', ctypes.c_uint16, 2),
|
||||
('reserved1', ctypes.c_uint16, 1),
|
||||
('no_soft_reset', ctypes.c_uint16, 1),
|
||||
('reserved2', ctypes.c_uint16, 4),
|
||||
('pme_en', ctypes.c_uint16, 1),
|
||||
('data_select', ctypes.c_uint16, 4),
|
||||
('data_scale', ctypes.c_uint16, 2),
|
||||
('pme_status', ctypes.c_uint16, 1),
|
||||
('reserved3', ctypes.c_uint16, 6),
|
||||
('undefined', ctypes.c_uint16, 2),
|
||||
('data', ctypes.c_uint8),
|
||||
]
|
||||
|
||||
def parse_power_management(buf, cap_ptr):
|
||||
return PowerManagement.from_buffer_copy(buf, cap_ptr)
|
||||
|
||||
# MSI (0x05)
|
||||
|
||||
def MSI_factory(field_list):
|
||||
class MSI(cdata.Struct, Capability):
|
||||
_pack_ = 1
|
||||
_fields_ = copy.copy(CapabilityListRegister._fields_) + [
|
||||
('msi_enable', ctypes.c_uint16, 1),
|
||||
('multiple_message_capable', ctypes.c_uint16, 3),
|
||||
('multiple_message_enable', ctypes.c_uint16, 3),
|
||||
('address_64bit', ctypes.c_uint16, 1),
|
||||
('per_vector_masking_capable', ctypes.c_uint16, 1),
|
||||
('reserved', ctypes.c_uint16, 7),
|
||||
] + field_list
|
||||
|
||||
return MSI
|
||||
|
||||
def msi_field_list(addr):
|
||||
field_list = list()
|
||||
msgctrl = MSI_factory([]).from_address(addr)
|
||||
|
||||
if msgctrl.address_64bit == 1:
|
||||
field_list.append(('message_address', ctypes.c_uint64))
|
||||
else:
|
||||
field_list.append(('message_address', ctypes.c_uint32))
|
||||
|
||||
field_list.append(('message_data', ctypes.c_uint16))
|
||||
|
||||
if msgctrl.per_vector_masking_capable:
|
||||
field_list.append(('reserved', ctypes.c_uint16))
|
||||
field_list.append(('mask_bits', ctypes.c_uint32))
|
||||
field_list.append(('pending_bits', ctypes.c_uint32))
|
||||
|
||||
return field_list
|
||||
|
||||
def parse_msi(buf, cap_ptr):
|
||||
addr = ctypes.addressof(buf) + cap_ptr
|
||||
field_list = msi_field_list(addr)
|
||||
return MSI_factory(field_list).from_buffer_copy(buf, cap_ptr)
|
||||
|
||||
# MSI-X
|
||||
|
||||
class MSIX(cdata.Struct, Capability):
|
||||
_pack_ = 1
|
||||
_fields_ = copy.copy(CapabilityListRegister._fields_) + [
|
||||
('table_size', ctypes.c_uint16, 10),
|
||||
('reserved', ctypes.c_uint16, 3),
|
||||
('function_mask', ctypes.c_uint16, 1),
|
||||
('msix_enable', ctypes.c_uint16, 1),
|
||||
('table_bir', ctypes.c_uint32, 3),
|
||||
('table_offset_z', ctypes.c_uint32, 29),
|
||||
('pba_bir', ctypes.c_uint32, 3),
|
||||
('pba_offset_z', ctypes.c_uint32, 29),
|
||||
]
|
||||
|
||||
@property
|
||||
def table_offset(self):
|
||||
return self.table_offset_z << 3
|
||||
|
||||
@property
|
||||
def pba_offset(self):
|
||||
return self.pba_offset_z << 3
|
||||
|
||||
def parse_msix(buf, cap_ptr):
|
||||
return MSIX.from_buffer_copy(buf, cap_ptr)
|
||||
|
||||
# Module API
|
||||
|
||||
capability_parsers = {
|
||||
0x1: parse_power_management,
|
||||
0x5: parse_msi,
|
||||
0x11: parse_msix,
|
||||
}
|
||||
|
||||
def capabilities(data, cap_ptr):
|
||||
buf = ctypes.create_string_buffer(data, len(data))
|
||||
|
||||
acc = list()
|
||||
while cap_ptr != 0:
|
||||
caplist = CapabilityListRegister.from_buffer_copy(buf, cap_ptr)
|
||||
if caplist.id in capability_parsers.keys():
|
||||
acc.append(capability_parsers[caplist.id](buf, cap_ptr))
|
||||
else:
|
||||
acc.append(caplist)
|
||||
cap_ptr = caplist.next_cap_ptr
|
||||
|
||||
return acc
|
189
misc/config_tools/board_inspector/pcieparser/header.py
Normal file
189
misc/config_tools/board_inspector/pcieparser/header.py
Normal file
@ -0,0 +1,189 @@
|
||||
# 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)
|
Loading…
Reference in New Issue
Block a user