mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-10-11 23:48:07 +00:00
While PCIe specification defines only type 0 and type 1 configuration space headers, there can be cases where a PCI function has a different header type. As an example, that device itself is under development or is a special emulated device. This patch makes the board inspector gracefully skips those PCIe functions and continue scanning the rest in such cases, as the only impact of the anomaly is the prevention of ACRN from passing through that PCIe function to any VM. It is an overkill to crash the board inspector. Tracked-On: #6689 Signed-off-by: Junjie Mao <junjie.mao@intel.com>
190 lines
5.7 KiB
Python
190 lines
5.7 KiB
Python
# Copyright (C) 2021-2022 Intel Corporation.
|
|
#
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
#
|
|
|
|
import ctypes
|
|
import copy
|
|
import inspectorlib.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 [('unparsed_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)
|