mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-07-31 15:30:56 +00:00
board_inspector: add interrupt pin routing and usage
This patch adds interrupt pin related information into the board XML, including: * The PCI routing table in ACPI DSDT/SSDT are parsed and generated into the board XML as "interrupt_pin_routing" nodes. * IRQs encoded in _CRS directly are represented as resources of type "irq". * Interrupt lines (i.e. INTx#) of PCI devices are represented as resources of type "interrupt_pin". When the PCI routing table is available, the corresponding interrupt line is identified and represented as the "source" attribute of the resource node. Due to the existence of vIOAPIC in ACRN VMs, the board inspector interprets the \_PIC method with parameter 1 to inform the ACPI namespace that the interrupt model should be in APIC mode. v1 -> v2: * Remove the msi_enable variable which is defined but never used. Tracked-On: #6287 Signed-off-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
parent
a3aa0797b1
commit
26021bd467
@ -611,6 +611,9 @@ def rdt_factory(field_list):
|
||||
for f in self._fields_:
|
||||
yield getattr(self, f[0])
|
||||
|
||||
def __getitem__(self, index):
|
||||
return getattr(self, self._fields_[index][0])
|
||||
|
||||
class ResourceData(cdata.Struct):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
|
@ -5,8 +5,10 @@
|
||||
|
||||
import logging
|
||||
import lxml.etree
|
||||
from collections import defaultdict
|
||||
|
||||
from acpiparser import parse_dsdt
|
||||
from acpiparser import parse_dsdt, parse_resource_data, parse_pci_routing
|
||||
import acpiparser.aml.context as context
|
||||
from acpiparser.aml.interpreter import ConcreteInterpreter
|
||||
from acpiparser.aml.exception import UndefinedSymbol, FutureWork
|
||||
from acpiparser.rdt import *
|
||||
@ -68,19 +70,20 @@ def get_device_element(devices_node, namepath, hid):
|
||||
element.set("id", hid)
|
||||
return element
|
||||
|
||||
def parse_irq(item, elem):
|
||||
add_child(elem, "resource", type="irq", int=hex(item._INT))
|
||||
def parse_irq(idx, item, elem):
|
||||
irqs = ", ".join(map(str, item.irqs))
|
||||
add_child(elem, "resource", id=f"res{idx}", type="irq", int=irqs)
|
||||
|
||||
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_io_port(idx, item, elem):
|
||||
add_child(elem, "resource", id=f"res{idx}", 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_io_port(idx, item, elem):
|
||||
add_child(elem, "resource", id=f"res{idx}", 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_fixed_memory_range(idx, item, elem):
|
||||
add_child(elem, "resource", id=f"res{idx}", type="memory", min=hex(item._BAS), max=hex(item._BAS + item._LEN - 1), len=hex(item._LEN))
|
||||
|
||||
def parse_address_space_resource(item, elem):
|
||||
def parse_address_space_resource(idx, item, elem):
|
||||
if item._TYP == 0:
|
||||
typ = "memory"
|
||||
elif item._TYP == 1:
|
||||
@ -89,17 +92,17 @@ def parse_address_space_resource(item, elem):
|
||||
typ = "bus_number"
|
||||
else:
|
||||
typ = "custom"
|
||||
add_child(elem, "resource", type=typ, min=hex(item._MIN), max=hex(item._MAX), len=hex(item._LEN))
|
||||
add_child(elem, "resource", id=f"res{idx}", 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))
|
||||
def parse_extended_irq(idx, item, elem):
|
||||
irqs = ", ".join(map(str, item._INT))
|
||||
add_child(elem, "resource", id=f"res{idx}", type="irq", int=irqs)
|
||||
|
||||
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),
|
||||
(0, SMALL_RESOURCE_ITEM_END_TAG): (lambda x,y,z: 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,
|
||||
@ -173,16 +176,41 @@ def fetch_device_info(devices_node, interpreter, namepath):
|
||||
data = interpreter.interpret_method_call(f"{namepath}._CRS").get()
|
||||
rdt = parse_resource_data(data)
|
||||
|
||||
for item in rdt.items:
|
||||
for idx, item in enumerate(rdt.items):
|
||||
p = (item.type, item.name)
|
||||
if p in resource_parsers.keys():
|
||||
resource_parsers[p](item, element)
|
||||
resource_parsers[p](idx, item, element)
|
||||
else:
|
||||
add_child(element, "resource", type=item.__class__.__name__)
|
||||
add_child(element, "resource", type=item.__class__.__name__, id=f"res{idx}")
|
||||
|
||||
# PCI interrupt routing
|
||||
if interpreter.context.has_symbol(f"{namepath}._PRT"):
|
||||
interpreter.interpret_method_call(f"{namepath}._PRT")
|
||||
pkg = interpreter.interpret_method_call(f"{namepath}._PRT")
|
||||
prt = parse_pci_routing(pkg)
|
||||
prt_info = defaultdict(lambda: {})
|
||||
for mapping in prt:
|
||||
if isinstance(mapping.source, int):
|
||||
assert mapping.source == 0, "A _PRT mapping package should not contain a byte of non-zero as source"
|
||||
prt_info[mapping.address][mapping.pin] = mapping.source_index
|
||||
elif isinstance(mapping.source, context.DeviceDecl):
|
||||
prt_info[mapping.address][mapping.pin] = (mapping.source.name, mapping.source_index)
|
||||
else:
|
||||
logging.warning(f"The _PRT of {namepath} has a mapping with invalid source {mapping.source}")
|
||||
|
||||
pin_routing_element = add_child(element, "interrupt_pin_routing")
|
||||
for address, pins in prt_info.items():
|
||||
mapping_element = add_child(pin_routing_element, "routing", address=hex(address))
|
||||
pin_names = {
|
||||
0: "INTA#",
|
||||
1: "INTB#",
|
||||
2: "INTC#",
|
||||
3: "INTD#",
|
||||
}
|
||||
for pin, info in pins.items():
|
||||
if isinstance(info, int):
|
||||
add_child(mapping_element, "mapping", pin=pin_names[pin], source=str(info))
|
||||
else:
|
||||
add_child(mapping_element, "mapping", pin=pin_names[pin], source=info[0], index=str(info[1]))
|
||||
|
||||
except FutureWork:
|
||||
pass
|
||||
@ -198,6 +226,14 @@ def extract(board_etree):
|
||||
return
|
||||
|
||||
interpreter = ConcreteInterpreter(namespace)
|
||||
|
||||
# With IOAPIC, Linux kernel will choose APIC mode as the IRQ model. Evalaute the \_PIC method (if exists) to inform the ACPI
|
||||
# namespace of this.
|
||||
try:
|
||||
interpreter.interpret_method_call("\\_PIC", 1)
|
||||
except:
|
||||
logging.info(f"\\_PIC is not evaluated.")
|
||||
|
||||
for device in sorted(namespace.devices, key=lambda x:x.name):
|
||||
try:
|
||||
fetch_device_info(devices_node, interpreter, device.name)
|
||||
|
@ -15,6 +15,13 @@ 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})$")
|
||||
|
||||
interrupt_pin_names = {
|
||||
1: "INTA#",
|
||||
2: "INTB#",
|
||||
3: "INTC#",
|
||||
4: "INTD#",
|
||||
}
|
||||
|
||||
def collect_hostbridge_resources(bus_node):
|
||||
with open("/proc/iomem", "r") as f:
|
||||
for line in f.readlines():
|
||||
@ -114,6 +121,17 @@ def parse_device(bus_node, device_path):
|
||||
if cap.name in cap_parsers:
|
||||
cap_parsers[cap.name](cap_node, cap)
|
||||
|
||||
# Interrupt pin
|
||||
pin = cfg.header.interrupt_pin
|
||||
if pin > 0 and pin <= 4:
|
||||
pin_name = interrupt_pin_names[pin]
|
||||
res_node = add_child(device_node, "resource", type="interrupt_pin", pin=pin_name)
|
||||
|
||||
prt_address = hex(int(device_node.get("address"), 16) | 0xffff)
|
||||
mapping = device_node.xpath(f"../interrupt_pin_routing/routing[@address='{prt_address}']/mapping[@pin='{pin_name}']")
|
||||
if len(mapping) > 0:
|
||||
res_node.set("source", mapping[0].get("source"))
|
||||
|
||||
# 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
|
||||
@ -134,6 +152,14 @@ def parse_device(bus_node, device_path):
|
||||
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))
|
||||
|
||||
# If a PCI routing table is provided for the root port / switch, move the routing table down to the bus node, in
|
||||
# order to align the relative position of devices and routing tables.
|
||||
prt = device_node.find("interrupt_pin_routing")
|
||||
if prt is not None:
|
||||
device_node.remove(prt)
|
||||
secondary_bus_node.append(prt)
|
||||
|
||||
return secondary_bus_node
|
||||
|
||||
return device_node
|
||||
|
@ -11,7 +11,7 @@ def getkey(child):
|
||||
if typ in ["memory", "io_port"]:
|
||||
return int(node.get("min"), base=16)
|
||||
elif typ == "irq":
|
||||
return int(node.get("int"), base=16)
|
||||
return int(node.get("int").split(", ")[0])
|
||||
else:
|
||||
return 0
|
||||
|
||||
@ -22,7 +22,9 @@ def getkey(child):
|
||||
else:
|
||||
return 0xFFFFFFFF
|
||||
|
||||
tags = ["vendor", "identifier", "subsystem_vendor", "subsystem_identifier", "class", "acpi_object", "status", "resource", "capability", "bus", "device"]
|
||||
tags = ["vendor", "identifier", "subsystem_vendor", "subsystem_identifier", "class",
|
||||
"acpi_object", "status",
|
||||
"resource", "capability", "interrupt_pin_routing", "bus", "device"]
|
||||
|
||||
if child.tag == "resource":
|
||||
return (tags.index(child.tag), child.get("type"), resource_subkey(child))
|
||||
|
@ -204,7 +204,7 @@ def get_devs_mem_passthrough(board_etree, scenario_etree):
|
||||
|
||||
def get_pci_hole_native(board_etree):
|
||||
resources = board_etree.xpath(f"//bus[@type = 'pci']/device[@address]/resource[@type = 'memory' and @len != '0x0']")
|
||||
resources_hostbridge = board_etree.xpath("//bus[@address = '0x0']/resource[@type = 'memory' and @len != '0x0' and not(@id) and not(@width)]")
|
||||
resources_hostbridge = board_etree.xpath("//bus[@address = '0x0']/resource[@type = 'memory' and @len != '0x0' and not(starts-with(@id, 'bar')) and not(@width)]")
|
||||
low_mem = set()
|
||||
high_mem = set()
|
||||
for resource_hostbridge in resources_hostbridge:
|
||||
|
Loading…
Reference in New Issue
Block a user