mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-08-06 18:55:40 +00:00
board_inspector/extractors: extract device information
This patch extracts information on devices and put them under the `/acrn-config/devices` node in the board XML. The generated XML looks like the following: <devices> <bus type="system"> <acpi_object>\_SB_</acpi_object> <bus id="PNP0A08" type="pci" address="0x0" description="..."> <vendor>0x8086</vendor> <identifier>0x591f</identifier> <subsystem_vendor>0x1028</subsystem_vendor> <subsystem_identifier>0x07a1</subsystem_identifier> <class>0x060000</class> <acpi_object>\_SB_.PCI0</acpi_object> <resource type="bus_number" min="0x0" max="0x3e" len="0x3f"/> <resource type="io_port" min="0x0" max="0xcf7" len="0xcf8"/> <resource type="io_port" min="0xcf8" max="0xcf8" len="0x8"/> <resource type="io_port" min="0xd00" max="0xffff" len="0xf300"/> <resource type="memory" min="0x10000" max="0x1ffff" len="0x0"/> <resource type="memory" min="0xa0000" max="0xbffff" len="0x20000"/> <resource type="memory" min="0xc0000" max="0xc3fff" len="0x4000"/> <resource type="memory" min="0xc4000" max="0xc7fff" len="0x4000"/> ... <capability id="vendor_specific"/> <device address="0x1"> ... </device> ... <bus> <bus> <device> ... <device> <devices> The hierarchy of devices are based on the hierarchy of device objects in the ACPI namespace (which is established by interpreting the ACPI DSDT and SSDT tables). Typically most device objects are under the predefined `_SB_` (i.e. System Bus) object under which an object representing the PCI root complex (`\_SB_.PCI0` in the example above) can be found. The PCI devices attached to bus 0 are listed as children of the PCI root complex node. For each bus or device, the board inspector tries best to parse the information from both ACPI device objects and PCI configuration space to extract the following: - the model (via `_HID` object and PCI vendor ID, device ID and class code), - assigned resources (via `_CRS` object and PCI BARs), - capabilities (via the PCI capability list) v1 -> v2: - Fix references to undeclared modules or variables. - Make the ACPI extractor advanced and not enabled by default. - Extract the secondary I/O and memory-mapped I/O addresses of bridges. Tracked-On: #5922 Signed-off-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
parent
52ee5827e1
commit
0aa899271d
202
misc/config_tools/board_inspector/extractors/50-acpi.py
Normal file
202
misc/config_tools/board_inspector/extractors/50-acpi.py
Normal file
@ -0,0 +1,202 @@
|
||||
# Copyright (C) 2021 Intel Corporation. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import logging
|
||||
import lxml.etree
|
||||
|
||||
from acpiparser import parse_dsdt
|
||||
from acpiparser.aml.interpreter import ConcreteInterpreter
|
||||
from acpiparser.aml.exception import UndefinedSymbol, FutureWork
|
||||
from acpiparser.rdt import *
|
||||
|
||||
from extractors.helpers import add_child, get_node
|
||||
|
||||
def parse_eisa_id(eisa_id):
|
||||
chars = [
|
||||
(eisa_id & 0x7c) >> 2, # Bit 6:2 of the first byte
|
||||
((eisa_id & 0x3) << 3) | ((eisa_id & 0xe000) >> 13), # Bit 1:0 of the first byte and bit 7:5 of the second
|
||||
(eisa_id & 0x1F00) >> 8, # Bit 4:0 of the second byte
|
||||
(eisa_id & 0x00F00000) >> 20, # Bit 7:4 of the third byte
|
||||
(eisa_id & 0x000F0000) >> 16, # Bit 3:0 of the third byte
|
||||
(eisa_id & 0xF0000000) >> 28, # Bit 7:4 of the fourth byte
|
||||
(eisa_id & 0x0F000000) >> 24, # Bit 3:0 of the fourth byte
|
||||
]
|
||||
if all(map(lambda x:x <= (ord('Z') - 0x40), chars[:3])):
|
||||
manufacturer = ''.join(map(lambda x: chr(x + 0x40), chars[:3]))
|
||||
product = ''.join(map(lambda x: "%X" % x, chars[3:6]))
|
||||
revision = "%X" % chars[6]
|
||||
return manufacturer + product + revision
|
||||
else:
|
||||
return None
|
||||
|
||||
predefined_nameseg = {
|
||||
"_SB_": ("bus", "system"),
|
||||
"_TZ_": ("thermalzone", None),
|
||||
}
|
||||
|
||||
buses = {
|
||||
"PNP0A03": "pci",
|
||||
"PNP0A08": "pci",
|
||||
}
|
||||
|
||||
def get_device_element(devices_node, namepath, hid):
|
||||
assert namepath.startswith("\\")
|
||||
namesegs = namepath[1:].split(".")
|
||||
|
||||
element = devices_node
|
||||
for i,nameseg in enumerate(namesegs):
|
||||
buspath = f"\\{'.'.join(namesegs[:(i+1)])}"
|
||||
tag, typ = "device", None
|
||||
if nameseg in predefined_nameseg.keys():
|
||||
tag, typ = predefined_nameseg[nameseg]
|
||||
next_element = None
|
||||
for child in element:
|
||||
acpi_object = get_node(child, "acpi_object")
|
||||
if acpi_object is not None and acpi_object.text == buspath:
|
||||
next_element = child
|
||||
break
|
||||
if next_element is None:
|
||||
next_element = add_child(element, tag, None)
|
||||
add_child(next_element, "acpi_object", buspath)
|
||||
if typ:
|
||||
next_element.set("type", typ)
|
||||
element = next_element
|
||||
|
||||
if hid:
|
||||
element.set("id", hid)
|
||||
return element
|
||||
|
||||
def parse_irq(item, elem):
|
||||
add_child(elem, "resource", type="irq", int=hex(item._INT))
|
||||
|
||||
def parse_io_port(item, elem):
|
||||
add_child(elem, "resource", type="io_port", min=hex(item._MIN), max=hex(item._MAX), len=hex(item._LEN))
|
||||
|
||||
def parse_fixed_io_port(item, elem):
|
||||
add_child(elem, "resource", type="io_port", min=hex(item._BAS), max=hex(item._BAS + item._LEN - 1), len=hex(item._LEN))
|
||||
|
||||
def parse_fixed_memory_range(item, elem):
|
||||
add_child(elem, "resource", type="memory", min=hex(item._BAS), max=hex(item._BAS + item._LEN - 1), len=hex(item._LEN))
|
||||
|
||||
def parse_address_space_resource(item, elem):
|
||||
if item._TYP == 0:
|
||||
typ = "memory"
|
||||
elif item._TYP == 1:
|
||||
typ = "io_port"
|
||||
elif item._TYP == 2:
|
||||
typ = "bus_number"
|
||||
else:
|
||||
typ = "custom"
|
||||
add_child(elem, "resource", type=typ, min=hex(item._MIN), max=hex(item._MAX), len=hex(item._LEN))
|
||||
|
||||
def parse_extended_irq(item, elem):
|
||||
for irq in item._INT:
|
||||
add_child(elem, "resource", type="irq", int=hex(irq))
|
||||
|
||||
resource_parsers = {
|
||||
(0, SMALL_RESOURCE_ITEM_IRQ_FORMAT): parse_irq,
|
||||
(0, SMALL_RESOURCE_ITEM_IO_PORT): parse_io_port,
|
||||
(0, SMALL_RESOURCE_ITEM_FIXED_LOCATION_IO_PORT): parse_fixed_io_port,
|
||||
(0, SMALL_RESOURCE_ITEM_END_TAG): (lambda x,y: None),
|
||||
(1, LARGE_RESOURCE_ITEM_32BIT_FIXED_MEMORY_RANGE): parse_fixed_memory_range,
|
||||
(1, LARGE_RESOURCE_ITEM_ADDRESS_SPACE_RESOURCE): parse_address_space_resource,
|
||||
(1, LARGE_RESOURCE_ITEM_WORD_ADDRESS_SPACE): parse_address_space_resource,
|
||||
(1, LARGE_RESOURCE_ITEM_EXTENDED_INTERRUPT): parse_extended_irq,
|
||||
(1, LARGE_RESOURCE_ITEM_QWORD_ADDRESS_SPACE): parse_address_space_resource,
|
||||
(1, LARGE_RESOURCE_ITEM_EXTENDED_ADDRESS_SPACE): parse_address_space_resource,
|
||||
}
|
||||
|
||||
def fetch_device_info(devices_node, interpreter, namepath):
|
||||
logging.info(f"Fetch information about device object {namepath}")
|
||||
|
||||
try:
|
||||
# Check if an _INI method exists
|
||||
try:
|
||||
interpreter.interpret_method_call(namepath + "._INI")
|
||||
except UndefinedSymbol:
|
||||
pass
|
||||
|
||||
sta = None
|
||||
if interpreter.context.has_symbol(f"{namepath}._STA"):
|
||||
sta = interpreter.interpret_method_call(f"{namepath}._STA").get()
|
||||
if sta & 0x1 == 0:
|
||||
return
|
||||
|
||||
# Hardware ID
|
||||
hid = ""
|
||||
if interpreter.context.has_symbol(f"{namepath}._HID"):
|
||||
hid = interpreter.interpret_method_call(f"{namepath}._HID").get()
|
||||
if isinstance(hid, str):
|
||||
pass
|
||||
elif isinstance(hid, int):
|
||||
eisa_id = parse_eisa_id(hid)
|
||||
if eisa_id:
|
||||
hid = eisa_id
|
||||
else:
|
||||
hid = hex(hid)
|
||||
else:
|
||||
hid = "<unknown>"
|
||||
|
||||
# Create the XML element for the device and create its ancestors if necessary
|
||||
element = get_device_element(devices_node, namepath, hid)
|
||||
if hid in buses.keys():
|
||||
element.tag = "bus"
|
||||
element.set("type", buses[hid])
|
||||
|
||||
# Address
|
||||
if interpreter.context.has_symbol(f"{namepath}._ADR"):
|
||||
adr = interpreter.interpret_method_call(f"{namepath}._ADR").get()
|
||||
if isinstance(adr, int):
|
||||
adr = hex(adr)
|
||||
if len(element.xpath(f"../*[@address='{adr}']")) > 0:
|
||||
logging.warning(f"{namepath} has siblings with duplicated address {adr}.")
|
||||
else:
|
||||
element.set("address", hex(adr) if isinstance(adr, int) else adr)
|
||||
|
||||
# Status
|
||||
if sta is not None:
|
||||
status = add_child(element, "status")
|
||||
|
||||
add_child(status, "present", "y" if sta & 0x1 != 0 else "n")
|
||||
add_child(status, "enabled", "y" if sta & 0x2 != 0 else "n")
|
||||
add_child(status, "functioning", "y" if sta & 0x8 != 0 else "n")
|
||||
|
||||
# Resources
|
||||
if interpreter.context.has_symbol(f"{namepath}._CRS"):
|
||||
data = interpreter.interpret_method_call(f"{namepath}._CRS").get()
|
||||
rdt = parse_resource_data(data)
|
||||
|
||||
for item in rdt.items:
|
||||
p = (item.type, item.name)
|
||||
if p in resource_parsers.keys():
|
||||
resource_parsers[p](item, element)
|
||||
else:
|
||||
add_child(element, "resource", type=item.__class__.__name__)
|
||||
|
||||
# PCI interrupt routing
|
||||
if interpreter.context.has_symbol(f"{namepath}._PRT"):
|
||||
interpreter.interpret_method_call(f"{namepath}._PRT")
|
||||
|
||||
except FutureWork:
|
||||
pass
|
||||
|
||||
def extract(board_etree):
|
||||
devices_node = get_node(board_etree, "//devices")
|
||||
|
||||
try:
|
||||
namespace = parse_dsdt()
|
||||
except Exception as e:
|
||||
logging.info(f"Parse ACPI DSDT/SSDT failed: {str(e)}")
|
||||
logging.info(f"Will not extract information from ACPI DSDT/SSDT")
|
||||
return
|
||||
|
||||
interpreter = ConcreteInterpreter(namespace)
|
||||
for device in sorted(namespace.devices, key=lambda x:x.name):
|
||||
try:
|
||||
fetch_device_info(devices_node, interpreter, device.name)
|
||||
except Exception as e:
|
||||
logging.info(f"Fetch information about device object {device.name} failed: {str(e)}")
|
||||
|
||||
advanced = True
|
150
misc/config_tools/board_inspector/extractors/60-pci.py
Normal file
150
misc/config_tools/board_inspector/extractors/60-pci.py
Normal file
@ -0,0 +1,150 @@
|
||||
# Copyright (C) 2021 Intel Corporation. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
import lxml.etree
|
||||
|
||||
from pcieparser import parse_config_space
|
||||
from pcieparser.header import IOBar, MemoryBar32, MemoryBar64
|
||||
from extractors.helpers import add_child, get_node
|
||||
|
||||
PCI_ROOT_PATH = "/sys/devices/pci0000:00"
|
||||
bdf_regex = re.compile(r"^([0-9a-f]{4}):([0-9a-f]{2}):([0-9a-f]{2}).([0-7]{1})$")
|
||||
|
||||
def collect_hostbridge_resources(bus_node):
|
||||
with open("/proc/iomem", "r") as f:
|
||||
for line in f.readlines():
|
||||
fields = line.strip().split(" : ")
|
||||
if fields[1] == "PCI Bus 0000:00":
|
||||
begin, end = tuple(map(lambda x: int(f"0x{x}", base=16), fields[0].split("-")))
|
||||
add_child(bus_node, "resource", type="memory", min=hex(begin), max=hex(end), len=hex(end - begin + 1))
|
||||
|
||||
def parse_msi(cap_node, cap_struct):
|
||||
if cap_struct.multiple_message_capable > 0:
|
||||
multiple_message_node = add_child(cap_node, "capability", id="multiple-message")
|
||||
add_child(multiple_message_node, "count", str(1 << cap_struct.multiple_message_capable))
|
||||
|
||||
if cap_struct.address_64bit:
|
||||
add_child(cap_node, "capability", id="64-bit address")
|
||||
|
||||
if cap_struct.per_vector_masking_capable:
|
||||
add_child(cap_node, "capability", id="per-vector masking")
|
||||
|
||||
cap_parsers = {
|
||||
"MSI": parse_msi,
|
||||
}
|
||||
|
||||
def parse_device(bus_node, device_path):
|
||||
device_name = os.path.basename(device_path)
|
||||
if device_name == "0000:00:00.0":
|
||||
device_node = bus_node
|
||||
else:
|
||||
m = bdf_regex.match(device_name)
|
||||
device, function = int(m.group(3), base=16), int(m.group(4), base=16)
|
||||
adr = hex((device << 16) + function)
|
||||
device_node = get_node(bus_node, f"./device[@address='{adr}']")
|
||||
if device_node is None:
|
||||
device_node = add_child(bus_node, "device", None, address=adr)
|
||||
|
||||
cfg = parse_config_space(device_path)
|
||||
|
||||
# Device identifiers
|
||||
vendor_id = "0x{:04x}".format(cfg.header.vendor_id)
|
||||
device_id = "0x{:04x}".format(cfg.header.device_id)
|
||||
class_code = "0x{:06x}".format(cfg.header.class_code)
|
||||
if device_node.get("id") is None:
|
||||
device_node.set("id", device_id)
|
||||
add_child(device_node, "vendor", vendor_id)
|
||||
add_child(device_node, "identifier", device_id)
|
||||
add_child(device_node, "class", class_code)
|
||||
if cfg.header.header_type == 0:
|
||||
subvendor_id = "0x{:04x}".format(cfg.header.subsystem_vendor_id)
|
||||
subdevice_id = "0x{:04x}".format(cfg.header.subsystem_device_id)
|
||||
add_child(device_node, "subsystem_vendor", subvendor_id)
|
||||
add_child(device_node, "subsystem_identifier", subdevice_id)
|
||||
|
||||
# BARs
|
||||
idx = 0
|
||||
for bar in cfg.header.bars:
|
||||
resource_path = os.path.join(device_path, f"resource{idx}")
|
||||
resource_type = bar.resource_type
|
||||
base = bar.base
|
||||
if os.path.exists(resource_path):
|
||||
resource_node = get_node(device_node, f"./resource[@type = '{resource_type}' and @min = '{hex(base)}']")
|
||||
if resource_node is None:
|
||||
size = os.path.getsize(resource_path)
|
||||
resource_node = add_child(device_node, "resource", None, type=resource_type, min=hex(base), max=hex(base + size - 1), len=hex(size))
|
||||
resource_node.set("id", f"bar{idx}")
|
||||
if isinstance(bar, MemoryBar32):
|
||||
resource_node.set("width", "32")
|
||||
resource_node.set("prefetchable", str(bar.prefetchable))
|
||||
elif isinstance(bar, MemoryBar64):
|
||||
resource_node.set("width", "64")
|
||||
resource_node.set("prefetchable", str(bar.prefetchable))
|
||||
elif bar.base != 0:
|
||||
logging.error(f"Cannot detect the size of BAR {idx}")
|
||||
if isinstance(bar, MemoryBar64):
|
||||
idx += 2
|
||||
else:
|
||||
idx += 1
|
||||
|
||||
# Capabilities
|
||||
for cap in cfg.caps:
|
||||
cap_node = add_child(device_node, "capability", id=cap.name)
|
||||
if cap.name in cap_parsers:
|
||||
cap_parsers[cap.name](cap_node, cap)
|
||||
|
||||
for cap in cfg.extcaps:
|
||||
cap_node = add_child(device_node, "capability", id=cap.name)
|
||||
if cap.name in cap_parsers:
|
||||
cap_parsers[cap.name](cap_node, cap)
|
||||
|
||||
# Secondary bus
|
||||
if cfg.header.header_type == 1:
|
||||
# According to section 3.2.5.6, PCI to PCI Bridge Architecture Specification, the I/O Limit register contains a
|
||||
# value smaller than the I/O Base register if there are no I/O addresses on the secondary side.
|
||||
io_base = (cfg.header.io_base_upper_16_bits << 16) | ((cfg.header.io_base >> 4) << 12)
|
||||
io_end = (cfg.header.io_limit_upper_16_bits << 16) | ((cfg.header.io_limit >> 4) << 12) | 0xfff
|
||||
if io_base <= io_end:
|
||||
add_child(device_node, "resource", type="io_port",
|
||||
min=hex(io_base), max=hex(io_end), len=hex(io_end - io_base + 1))
|
||||
|
||||
# According to section 3.2.5.8, PCI to PCI Bridge Architecture Specification, the Memory Limit register contains
|
||||
# a value smaller than the Memory Base register if there are no memory-mapped I/O addresses on the secondary
|
||||
# side.
|
||||
if cfg.header.memory_base <= cfg.header.memory_limit:
|
||||
memory_base = (cfg.header.memory_base >> 4) << 20
|
||||
memory_end = ((cfg.header.memory_limit >> 4) << 20) | 0xfffff
|
||||
add_child(device_node, "resource", type="memory",
|
||||
min=hex(memory_base), max=hex(memory_end), len=hex(memory_end - memory_base + 1))
|
||||
|
||||
secondary_bus_node = add_child(device_node, "bus", type="pci", address=hex(cfg.header.secondary_bus_number))
|
||||
return secondary_bus_node
|
||||
|
||||
return device_node
|
||||
|
||||
def enum_devices(bus_node, root_path):
|
||||
device_names = sorted(filter(lambda x:bdf_regex.match(x) != None, os.listdir(root_path)))
|
||||
for device_name in device_names:
|
||||
p = os.path.join(root_path, device_name)
|
||||
device_node = parse_device(bus_node, p)
|
||||
enum_devices(device_node, p)
|
||||
|
||||
def extract(board_etree):
|
||||
bus_node = get_node(board_etree, "//bus[@type='pci']")
|
||||
if bus_node is None:
|
||||
devices_node = get_node(board_etree, "//devices")
|
||||
bus_node = add_child(devices_node, "bus", type="pci", address="0x0")
|
||||
collect_hostbridge_resources(bus_node)
|
||||
else:
|
||||
# Assume there is only one device object in the ACPI DSDT that represents a PCI bridge (which should be the host
|
||||
# bridge in this case). If the ACPI table does not provide an _ADR object, add the default address of the host
|
||||
# bridge (i.e. bus 0).
|
||||
if bus_node.get("address") is None:
|
||||
bus_node.set("address", "0x0")
|
||||
|
||||
enum_devices(bus_node, PCI_ROOT_PATH)
|
123
misc/config_tools/board_inspector/extractors/80-lookup.py
Normal file
123
misc/config_tools/board_inspector/extractors/80-lookup.py
Normal file
@ -0,0 +1,123 @@
|
||||
# Copyright (C) 2021 Intel Corporation. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import os
|
||||
import logging
|
||||
|
||||
from extractors.helpers import get_node
|
||||
|
||||
PCI_IDS_PATHS = [
|
||||
"/usr/share/misc/pci.ids",
|
||||
"/usr/share/hwdata/pci.ids",
|
||||
]
|
||||
|
||||
class PCI_IDs:
|
||||
def __init__(self, f):
|
||||
names = {}
|
||||
vendor_id = None
|
||||
device_id = None
|
||||
classes = {}
|
||||
class_id = None
|
||||
|
||||
for line in f.readlines():
|
||||
line = line.strip("\n")
|
||||
if line == "" or line.startswith("#"):
|
||||
continue
|
||||
|
||||
if line.startswith("\t\t"):
|
||||
if device_id is not None:
|
||||
parts = line.strip().split(" ")
|
||||
subvendor_id, subdevice_id = tuple(map(lambda x: int(x, base=16), parts[0].split(" ")))
|
||||
desc = " ".join(parts[1:])
|
||||
names[(vendor_id, device_id, subvendor_id, subdevice_id)] = desc
|
||||
elif line.startswith("\t"):
|
||||
if vendor_id is not None:
|
||||
parts = line.strip().split(" ")
|
||||
device_id = int(parts[0], base=16)
|
||||
desc = " ".join(parts[1:])
|
||||
names[(vendor_id, device_id)] = desc
|
||||
elif class_id is not None:
|
||||
parts = line.strip().split(" ")
|
||||
subclass_id = int(parts[0], base=16)
|
||||
desc = " ".join(parts[1:])
|
||||
classes[(class_id, subclass_id)] = desc
|
||||
elif line.startswith("C"):
|
||||
parts = line.strip().split(" ")
|
||||
class_id = int(parts[0][2:], base=16)
|
||||
vendor_id = None
|
||||
device_id = None
|
||||
desc = " ".join(parts[1:])
|
||||
classes[(class_id,)] = desc
|
||||
else:
|
||||
parts = line.strip().split(" ")
|
||||
vendor_id = int(parts[0], base=16)
|
||||
device_id = None
|
||||
class_id = None
|
||||
desc = " ".join(parts[1:])
|
||||
names[(vendor_id,)] = desc
|
||||
|
||||
self.__names = names
|
||||
self.__classes = classes
|
||||
|
||||
def lookup(self, vendor_id, device_id, subsystem_vendor_id, subsystem_device_id, class_code):
|
||||
acc = []
|
||||
|
||||
# Class
|
||||
if class_code:
|
||||
class_id = (class_code >> 16)
|
||||
subclass_id = ((class_code >> 8) & 0xFF)
|
||||
if (class_id, subclass_id) in self.__classes.keys():
|
||||
acc.append(self.__classes[(class_id, subclass_id)] + ":")
|
||||
elif (class_id,) in self.__classes.keys():
|
||||
acc.append(self.__classes[(class_id,)] + ":")
|
||||
|
||||
# Vendor
|
||||
if vendor_id:
|
||||
if (vendor_id,) in self.__names.keys():
|
||||
acc.append(self.__names[(vendor_id,)])
|
||||
|
||||
# Device
|
||||
if vendor_id and device_id:
|
||||
if (vendor_id, device_id) in self.__names.keys():
|
||||
acc.append(self.__names[(vendor_id, device_id)])
|
||||
|
||||
return " ".join(acc)
|
||||
|
||||
def lookup_pci_device(element, ids):
|
||||
vendor_id = get_node(element, "vendor/text()")
|
||||
device_id = get_node(element, "identifier/text()")
|
||||
subsystem_vendor_id = get_node(element, "subsystem_vendor/text()")
|
||||
subsystem_device_id = get_node(element, "subsystem_identifier/text()")
|
||||
class_code = get_node(element, "class/text()")
|
||||
|
||||
args = [vendor_id, device_id, subsystem_vendor_id, subsystem_device_id, class_code]
|
||||
desc = ids.lookup(*list(map(lambda x: int(x, base=16) if x else None, args)))
|
||||
|
||||
if desc:
|
||||
element.set("description", desc)
|
||||
|
||||
def lookup_pci_devices(board_etree):
|
||||
# Lookup names of PCI devices from pci.ids if possible
|
||||
pci_id_path = None
|
||||
for path in PCI_IDS_PATHS:
|
||||
if os.path.exists(path):
|
||||
pci_id_path = path
|
||||
|
||||
if pci_id_path:
|
||||
with open(pci_id_path, "r") as f:
|
||||
ids = PCI_IDs(f)
|
||||
|
||||
devices = board_etree.xpath("//device")
|
||||
for device in devices:
|
||||
lookup_pci_device(device, ids)
|
||||
|
||||
buses = board_etree.xpath("//bus")
|
||||
for bus in buses:
|
||||
lookup_pci_device(bus, ids)
|
||||
else:
|
||||
logging.info(f"Cannot find pci.ids under /usr/share. PCI device names will not be available.")
|
||||
|
||||
def extract(board_etree):
|
||||
lookup_pci_devices(board_etree)
|
41
misc/config_tools/board_inspector/extractors/90-sorting.py
Normal file
41
misc/config_tools/board_inspector/extractors/90-sorting.py
Normal file
@ -0,0 +1,41 @@
|
||||
# Copyright (C) 2021 Intel Corporation. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import lxml.etree
|
||||
|
||||
def getkey(child):
|
||||
def resource_subkey(node):
|
||||
typ = node.get("type")
|
||||
if typ in ["memory", "io_port"]:
|
||||
return int(node.get("min"), base=16)
|
||||
elif typ == "irq":
|
||||
return int(node.get("int"), base=16)
|
||||
else:
|
||||
return 0
|
||||
|
||||
def device_subkey(node):
|
||||
adr = node.get("address")
|
||||
if adr is not None:
|
||||
return int(adr, base=16)
|
||||
else:
|
||||
return 0xFFFFFFFF
|
||||
|
||||
tags = ["vendor", "identifier", "subsystem_vendor", "subsystem_identifier", "class", "acpi_object", "status", "resource", "capability", "bus", "device"]
|
||||
|
||||
if child.tag == "resource":
|
||||
return (tags.index(child.tag), child.get("type"), resource_subkey(child))
|
||||
elif child.tag == "device":
|
||||
return (tags.index(child.tag), device_subkey(child))
|
||||
else:
|
||||
return (tags.index(child.tag),)
|
||||
|
||||
def extract(board_etree):
|
||||
# Sort children of bus and device nodes
|
||||
bus_nodes = board_etree.xpath("//bus")
|
||||
for bus_node in bus_nodes:
|
||||
bus_node[:] = sorted(bus_node, key=getkey)
|
||||
device_nodes = board_etree.xpath("//device")
|
||||
for device_node in device_nodes:
|
||||
device_node[:] = sorted(device_node, key=getkey)
|
90
misc/config_tools/board_inspector/pcieparser/extcaps.py
Normal file
90
misc/config_tools/board_inspector/pcieparser/extcaps.py
Normal file
@ -0,0 +1,90 @@
|
||||
# Copyright (C) 2021 Intel Corporation. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import ctypes
|
||||
import copy
|
||||
import lib.cdata as cdata
|
||||
|
||||
class ExtendedCapability:
|
||||
# Capability names from PCI Express Base Specification, mostly Table 9-23
|
||||
_cap_names_ = {
|
||||
0x01: "Advanced Error Reporting",
|
||||
0x02: "Virtual Channel",
|
||||
0x03: "Device Serial Number",
|
||||
0x04: "Power Budgeting",
|
||||
0x05: "Root Complex Link Declaration",
|
||||
0x06: "Root Complex Internal Link Control",
|
||||
0x07: "Root Complex Event Collector Endpoint Association",
|
||||
0x08: "Multi-Function Virtual Channel",
|
||||
0x09: "Virtual Channel",
|
||||
0x0a: "RCRB Header",
|
||||
0x0b: "Vendor-Specific Extended",
|
||||
0x0c: "Configuration Access Correlation",
|
||||
0x0d: "ACS",
|
||||
0x0e: "ARI",
|
||||
0x0f: "ATS",
|
||||
0x10: "SR-IOV",
|
||||
0x11: "MR-IOV",
|
||||
0x12: "Multicast",
|
||||
0x13: "PRI",
|
||||
0x15: "Resizable BAR",
|
||||
0x16: "DPA",
|
||||
0x17: "TPH Requester",
|
||||
0x18: "LTR",
|
||||
0x19: "Secondary PCI Express",
|
||||
0x1a: "PMUX",
|
||||
0x1b: "PASID",
|
||||
0x1c: "LNR",
|
||||
0x1d: "DPC",
|
||||
0x1e: "L1 PM Substates",
|
||||
0x1f: "TPM",
|
||||
0x20: "M-PCIe",
|
||||
0x21: "FRS Queueing",
|
||||
0x22: "Readiness Time Reporting",
|
||||
0x23: "Designated Vendor-Specific",
|
||||
0x24: "VF Resizable BAR",
|
||||
0x25: "Data Link Feature",
|
||||
0x26: "Physical Layer 16.0 GT/s",
|
||||
0x27: "Lane Margining at the Receiver",
|
||||
0x28: "Hierarchy ID",
|
||||
0x29: "NPEM",
|
||||
0x2a: "Physical Layer 32.0 GT/s",
|
||||
0x2b: "Alternate Protocol",
|
||||
0x2c: "SFI",
|
||||
}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
if self.id in self._cap_names_.keys():
|
||||
return self._cap_names_[self.id]
|
||||
else:
|
||||
return f"Reserved Extended ({hex(self.id)})"
|
||||
|
||||
class ExtendedCapabilityListRegister(cdata.Struct, ExtendedCapability):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('id', ctypes.c_uint32, 16),
|
||||
('version', ctypes.c_uint32, 4),
|
||||
('next_cap_ptr_raw', ctypes.c_uint32, 12),
|
||||
]
|
||||
|
||||
@property
|
||||
def next_cap_ptr(self):
|
||||
return self.next_cap_ptr_raw & 0xffc
|
||||
|
||||
# Module API
|
||||
|
||||
def extended_capabilities(data):
|
||||
buf = ctypes.create_string_buffer(data, len(data))
|
||||
cap_ptr = 0x100
|
||||
|
||||
acc = list()
|
||||
while cap_ptr != 0:
|
||||
caplist = ExtendedCapabilityListRegister.from_buffer_copy(buf, cap_ptr)
|
||||
if caplist.id != 0:
|
||||
acc.append(caplist)
|
||||
cap_ptr = caplist.next_cap_ptr
|
||||
|
||||
return acc
|
Loading…
Reference in New Issue
Block a user