diff --git a/misc/config_tools/static_allocators/intx.py b/misc/config_tools/static_allocators/intx.py index f113d63d6..50b7c5daf 100644 --- a/misc/config_tools/static_allocators/intx.py +++ b/misc/config_tools/static_allocators/intx.py @@ -8,6 +8,9 @@ import sys, os sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'library')) import common, board_cfg_lib, lib.error, lib.lib +import re +from collections import defaultdict +from itertools import combinations LEGACY_IRQ_MAX = 16 @@ -47,7 +50,7 @@ def create_vuart_irq_node(etree, vm_id, vuart_id, irq): common.append_node(f"./legacy_vuart[@id = '{vuart_id}']/irq", irq, allocation_sos_vm_node) -def fn(board_etree, scenario_etree, allocation_etree): +def alloc_sos_vuart_irqs(board_etree, scenario_etree, allocation_etree): irq_list = get_native_valid_irq() hv_debug_console = lib.lib.parse_hv_console(scenario_etree) native_ttys = lib.lib.get_native_ttys() @@ -69,3 +72,133 @@ def fn(board_etree, scenario_etree, allocation_etree): vuart1_irq = alloc_irq(irq_list) create_vuart_irq_node(allocation_etree, vm_id, "1", vuart1_irq) + +def get_irqs_of_device(device_node): + irqs = set() + + # IRQs in ACPI + for res in device_node.xpath("resource[@type='irq']"): + irqs.update(set(map(int, res.get("int").split(", ")))) + + # PCI interrupt pin + for res in device_node.xpath("resource[@type='interrupt_pin']"): + irq = res.get("source", None) + if irq is not None: + irqs.add(int(irq)) + + return irqs + +def alloc_device_irqs(board_etree, scenario_etree, allocation_etree): + service_vm_id = -1 + irq_allocation = defaultdict(lambda: defaultdict(lambda: [])) # vm_id -> irq -> [device] + + # Collect the list of devices that have to use INTx, excluding legacy UART which is to be emulated. + device_nodes = set(board_etree.xpath("//device[count(resource[@type='irq' or @type='interrupt_pin']) > 0 and count(capability[@id='MSI' or @id='MSI-X']) = 0]")) + uart_nodes = set(board_etree.xpath("//device[@id='PNP0501']")) + device_nodes -= uart_nodes + + # + # Identify the interrupt lines each pre-launched VM uses + # + for vm in scenario_etree.xpath("//vm"): + vm_type = vm.find("vm_type").text + vm_id = int(vm.get("id")) + if lib.lib.is_pre_launched_vm(vm_type): + pt_intx_node = common.get_node("pt_intx", vm) + if pt_intx_node is not None: + pt_intx_mapping = dict(eval(f"[{pt_intx_node.text.replace(')(', '), (')}]")) + for irq in pt_intx_mapping.keys(): + irq_allocation[vm_id][irq].append("(Explicitly assigned in scenario configuration)") + for pci_dev in vm.xpath("pci_devs/pci_dev/text()"): + bdf = lib.lib.BusDevFunc.from_str(pci_dev.split(" ")[0]) + address = hex((bdf.dev << 16) | (bdf.func)) + device_node = common.get_node(f"//bus[@address='{hex(bdf.bus)}']/device[@address='{address}']", board_etree) + if device_node in device_nodes: + irqs = get_irqs_of_device(device_node) + for irq in irqs: + irq_allocation[vm_id][irq].append(pci_dev) + device_nodes.discard(device_node) + + # Raise error when any pre-launched VM with LAPIC passthrough requires any interrupt line. + lapic_passthru_flag = common.get_node("guest_flags[guest_flag='GUEST_FLAG_LAPIC_PASSTHROUGH']", vm) + if lapic_passthru_flag is not None and irq_allocation[vm_id]: + for irq, devices in irq_allocation[vm_id].items(): + print(f"Interrupt line {irq} is used by the following device(s).") + for device in devices: + print(f"\t{device}") + raise lib.error.ResourceError(f"Pre-launched VM {vm_id} with LAPIC_PASSTHROUGH flag cannot use interrupt lines.") + elif lib.lib.is_sos_vm(vm_type): + service_vm_id = vm_id + + # + # Detect interrupt line conflicts + # + conflicts = defaultdict(lambda: defaultdict(lambda: set())) # irq -> vm_id -> devices + + # If a service VM exists, collect its interrupt lines as well + if service_vm_id >= 0: + # Collect the interrupt lines that may be used by the service VM + for device_node in device_nodes: + acpi_object = device_node.find("acpi_object") + description = "" + if acpi_object is not None: + description = acpi_object.text + description = device_node.get("description", description) + + # Guess BDF of the device + bus = device_node.getparent() + if bus.tag == "bus" and bus.get("type") == "pci" and device_node.get("address") is not None: + bus_number = int(bus.get("address"), 16) + address = int(device_node.get("address"), 16) + device_number = address >> 16 + function_number = address & 0xffff + description = f"{bus_number:02x}:{device_number:02x}.{function_number} {description}" + + for irq in get_irqs_of_device(device_node): + irq_allocation[service_vm_id][irq].append(description) + + # Identify and report conflicts among interrupt lines of the VMs + for vm1, vm2 in combinations(irq_allocation.keys(), 2): + common_irqs = set(irq_allocation[vm1].keys()) & set(irq_allocation[vm2].keys()) + for irq in common_irqs: + conflicts[irq][vm1].update(set(irq_allocation[vm1][irq])) + conflicts[irq][vm2].update(set(irq_allocation[vm2][irq])) + + if conflicts: + print("Interrupt line conflicts detected!") + for irq, vm_devices in sorted(conflicts.items()): + print(f"Interrupt line {irq} is shared by the following devices.") + for vm_id, devices in vm_devices.items(): + for device in sorted(devices): + print(f"\tVM {vm_id}: {device}") + raise lib.error.ResourceError(f"VMs have conflicting interrupt lines.") + + # + # Dump allocations to allocation_etree. The virtual interrupt line is the same as the physical one unless otherwise + # stated in the scenario configuration. + # + for vm_id, alloc in irq_allocation.items(): + vm_node = common.get_node(f"/acrn-config/vm[@id = '{vm_id}']", allocation_etree) + if vm_node is None: + vm_node = common.append_node("/acrn-config/vm", None, allocation_etree, id = str(vm_id)) + pt_intx_node = common.get_node(f"//vm[@id='{vm_id}']/pt_intx", scenario_etree) + pt_intx_mapping = dict(eval(f"[{pt_intx_node.text.replace(')(', '), (')}]")) if pt_intx_node is not None else {} + for irq, devs in alloc.items(): + for dev in devs: + if dev.startswith("("): # Allocation in the scenario configuration need not go to allocation.xml + continue + bdf = dev.split(" ")[0] + dev_name = f"PTDEV_{bdf}" + dev_node = common.get_node(f"device[@name = '{dev_name}']", vm_node) + if dev_node is None: + dev_node = common.append_node("./device", None, vm_node, name = dev_name) + pt_intx_node = common.get_node(f"pt_intx", dev_node) + virq = pt_intx_mapping.get(irq, irq) + if pt_intx_node is None: + common.append_node(f"./pt_intx", f"({irq}, {virq})", dev_node) + else: + pt_intx_node.text += f" ({irq}, {virq})" + +def fn(board_etree, scenario_etree, allocation_etree): + alloc_sos_vuart_irqs(board_etree, scenario_etree, allocation_etree) + alloc_device_irqs(board_etree, scenario_etree, allocation_etree) diff --git a/misc/config_tools/xforms/lib.xsl b/misc/config_tools/xforms/lib.xsl index 4873fe86e..ce51b9ef2 100644 --- a/misc/config_tools/xforms/lib.xsl +++ b/misc/config_tools/xforms/lib.xsl @@ -9,6 +9,8 @@ xmlns:dyn="http://exslt.org/dynamic" xmlns:math="http://exslt.org/math" xmlns:func="http://exslt.org/functions" + xmlns:str="http://exslt.org/strings" + xmlns:set="http://exslt.org/sets" xmlns:acrn="http://projectacrn.org" extension-element-prefixes="func"> @@ -586,6 +588,13 @@ + + + + + + + diff --git a/misc/config_tools/xforms/misc_cfg.h.xsl b/misc/config_tools/xforms/misc_cfg.h.xsl index bd2a384b7..62756739e 100644 --- a/misc/config_tools/xforms/misc_cfg.h.xsl +++ b/misc/config_tools/xforms/misc_cfg.h.xsl @@ -39,7 +39,6 @@ - @@ -216,17 +215,4 @@ - - - - - - - - - - - - - diff --git a/misc/config_tools/xforms/pt_intx.c.xsl b/misc/config_tools/xforms/pt_intx.c.xsl index 3a42e611d..5afad4945 100644 --- a/misc/config_tools/xforms/pt_intx.c.xsl +++ b/misc/config_tools/xforms/pt_intx.c.xsl @@ -20,20 +20,7 @@ - - - - - - - - - - - struct pt_intx_config vm0_pt_intx[1U]; - - - + @@ -51,26 +38,33 @@ }, - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/misc/config_tools/xforms/vm_configurations.c.xsl b/misc/config_tools/xforms/vm_configurations.c.xsl index 5554b1ec0..45cdd1c69 100644 --- a/misc/config_tools/xforms/vm_configurations.c.xsl +++ b/misc/config_tools/xforms/vm_configurations.c.xsl @@ -32,19 +32,21 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -253,9 +255,12 @@ }, - - + + + + +