diff --git a/misc/config_tools/data/cfl-k700-i7/hybrid.xml b/misc/config_tools/data/cfl-k700-i7/hybrid.xml index 5634c505a..975d03c51 100644 --- a/misc/config_tools/data/cfl-k700-i7/hybrid.xml +++ b/misc/config_tools/data/cfl-k700-i7/hybrid.xml @@ -32,8 +32,7 @@ y n n - - + 0x2000 @@ -67,13 +66,12 @@ - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM SAFETY_VM0 3 - 0 @@ -106,10 +104,11 @@ n n + - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -146,8 +145,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 2 @@ -170,8 +169,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM2 2 diff --git a/misc/config_tools/data/cfl-k700-i7/hybrid_rt.xml b/misc/config_tools/data/cfl-k700-i7/hybrid_rt.xml index b8d6bd03b..0d329d179 100644 --- a/misc/config_tools/data/cfl-k700-i7/hybrid_rt.xml +++ b/misc/config_tools/data/cfl-k700-i7/hybrid_rt.xml @@ -73,8 +73,8 @@ - RTVM PRE_LAUNCHED_VM + RTVM PRE_RT_VM0 y @@ -113,18 +113,26 @@ INVALID_PCI_BASE + + n + 00:1f.6 Ethernet controller: Intel Corporation Ethernet Connection (7) I219-LM (rev 10) 01:00.0 Non-Volatile memory controller: Silicon Motion, Inc. SM2263EN/SM2263XT SSD Controller (rev 03) - - n - - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM + + 0 + 1 + 2 + 3 + 4 + 5 + 0 0 @@ -143,14 +151,6 @@ root=/dev/nvme0n1p3 rw rootwait console=ttyS0,115200n8 ignore_loglevel no_timer_check - - 0 - 1 - 2 - 3 - 4 - 5 - VUART_LEGACY_PIO SERVICE_VM_COM1_BASE @@ -164,8 +164,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 1 @@ -192,8 +192,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM2 4 diff --git a/misc/config_tools/data/cfl-k700-i7/partitioned.xml b/misc/config_tools/data/cfl-k700-i7/partitioned.xml index 32071cf89..f2297e51b 100644 --- a/misc/config_tools/data/cfl-k700-i7/partitioned.xml +++ b/misc/config_tools/data/cfl-k700-i7/partitioned.xml @@ -32,8 +32,7 @@ y n n - - + 0x2000 @@ -67,8 +66,8 @@ - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM PRE_STD_VM0 0 @@ -104,18 +103,18 @@ INVALID_PCI_BASE + + n + 00:17.0 SATA controller: Intel Corporation Cannon Lake PCH SATA AHCI Controller (rev 10) 00:1f.6 Ethernet controller: Intel Corporation Ethernet Connection (7) I219-LM (rev 10) - - n - - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM PRE_STD_VM1 1 @@ -152,12 +151,12 @@ INVALID_PCI_BASE + + n + 00:14.0 USB controller: Intel Corporation Cannon Lake PCH USB 3.1 xHCI Host Controller (rev 10) - - n - diff --git a/misc/config_tools/data/cfl-k700-i7/shared.xml b/misc/config_tools/data/cfl-k700-i7/shared.xml index 5c088c4cb..726b4f05f 100644 --- a/misc/config_tools/data/cfl-k700-i7/shared.xml +++ b/misc/config_tools/data/cfl-k700-i7/shared.xml @@ -24,8 +24,7 @@ y n n - - + 0x2000 @@ -60,8 +59,8 @@ - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -91,8 +90,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 0 @@ -116,8 +115,8 @@ - RTVM POST_LAUNCHED_VM + RTVM POST_RT_VM1 2 @@ -142,8 +141,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM2 0 @@ -168,8 +167,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM3 0 @@ -194,8 +193,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM4 0 @@ -220,8 +219,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM5 0 diff --git a/misc/config_tools/data/generic_board/hybrid.xml b/misc/config_tools/data/generic_board/hybrid.xml index bdff6a8e1..d6b3fd8a6 100644 --- a/misc/config_tools/data/generic_board/hybrid.xml +++ b/misc/config_tools/data/generic_board/hybrid.xml @@ -32,8 +32,7 @@ y n n - - + 0x2000 @@ -67,13 +66,12 @@ - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM SAFETY_VM0 3 - 0 @@ -102,17 +100,18 @@ INVALID_PCI_BASE - - - n n + + + + - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -148,8 +147,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 2 @@ -172,8 +171,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM2 2 diff --git a/misc/config_tools/data/generic_board/hybrid_rt.xml b/misc/config_tools/data/generic_board/hybrid_rt.xml index 675199db9..5ce6c4f8e 100644 --- a/misc/config_tools/data/generic_board/hybrid_rt.xml +++ b/misc/config_tools/data/generic_board/hybrid_rt.xml @@ -86,8 +86,8 @@ - RTVM PRE_LAUNCHED_VM + RTVM PRE_RT_VM0 y @@ -123,17 +123,17 @@ INVALID_PCI_BASE + + n + 00:17.0 SATA controller: Intel Corporation Device a0d3 (rev 20) 58:00.0 Ethernet controller: Intel Corporation Device 15f2 (rev 03) - - n - - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -167,8 +167,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 0 @@ -193,8 +193,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM2 1 diff --git a/misc/config_tools/data/generic_board/partitioned.xml b/misc/config_tools/data/generic_board/partitioned.xml index 604b5facd..960154afa 100644 --- a/misc/config_tools/data/generic_board/partitioned.xml +++ b/misc/config_tools/data/generic_board/partitioned.xml @@ -32,8 +32,7 @@ y n n - - + 0x2000 @@ -67,8 +66,8 @@ - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM PRE_STD_VM0 0 @@ -104,18 +103,18 @@ INVALID_PCI_BASE + + n + 00:17.0 SATA controller: Intel Corporation Device a0d3 (rev 20) 58:00.0 Ethernet controller: Intel Corporation Device 15f2 (rev 03) - - n - - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM PRE_STD_VM1 1 @@ -152,12 +151,12 @@ INVALID_PCI_BASE + + n + 00:14.0 USB controller: Intel Corporation Device a0ed (rev 20) - - n - diff --git a/misc/config_tools/data/generic_board/shared.xml b/misc/config_tools/data/generic_board/shared.xml index 468ee62b4..879b106ee 100644 --- a/misc/config_tools/data/generic_board/shared.xml +++ b/misc/config_tools/data/generic_board/shared.xml @@ -32,8 +32,7 @@ y n n - - + 0x2000 @@ -67,8 +66,8 @@ - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -98,8 +97,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 0 @@ -124,8 +123,8 @@ n - RTVM POST_LAUNCHED_VM + RTVM POST_RT_VM1 y @@ -151,8 +150,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM2 0 @@ -177,8 +176,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM3 0 @@ -203,8 +202,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM4 0 @@ -229,8 +228,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM5 0 diff --git a/misc/config_tools/data/nuc11tnbi5/hybrid.xml b/misc/config_tools/data/nuc11tnbi5/hybrid.xml index f30a9303d..786afc414 100644 --- a/misc/config_tools/data/nuc11tnbi5/hybrid.xml +++ b/misc/config_tools/data/nuc11tnbi5/hybrid.xml @@ -32,8 +32,7 @@ y n n - - + 0x2000 @@ -67,13 +66,12 @@ - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM SAFETY_VM0 3 - 0 @@ -102,17 +100,18 @@ INVALID_PCI_BASE - - - n n + + + + - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -148,8 +147,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 2 @@ -172,8 +171,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM2 2 diff --git a/misc/config_tools/data/nuc11tnbi5/partitioned.xml b/misc/config_tools/data/nuc11tnbi5/partitioned.xml index c0e1e3023..d01612cb6 100644 --- a/misc/config_tools/data/nuc11tnbi5/partitioned.xml +++ b/misc/config_tools/data/nuc11tnbi5/partitioned.xml @@ -32,8 +32,7 @@ y n n - - + 0x2000 @@ -67,8 +66,8 @@ - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM PRE_STD_VM0 0 @@ -104,18 +103,18 @@ INVALID_PCI_BASE + + n + 00:17.0 SATA controller: Intel Corporation Device a0d3 (rev 20) 58:00.0 Ethernet controller: Intel Corporation Device 15f2 (rev 03) - - n - - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM PRE_STD_VM1 1 @@ -152,12 +151,12 @@ INVALID_PCI_BASE + + n + 00:14.0 USB controller: Intel Corporation Device a0ed (rev 20) - - n - diff --git a/misc/config_tools/data/nuc11tnbi5/shared.xml b/misc/config_tools/data/nuc11tnbi5/shared.xml index 1e47addd3..aabaec630 100644 --- a/misc/config_tools/data/nuc11tnbi5/shared.xml +++ b/misc/config_tools/data/nuc11tnbi5/shared.xml @@ -32,8 +32,7 @@ y n n - - + 0x2000 @@ -79,8 +78,8 @@ - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -110,8 +109,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 0 @@ -136,8 +135,8 @@ n - RTVM POST_LAUNCHED_VM + RTVM POST_RT_VM1 y @@ -163,8 +162,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM2 0 @@ -189,8 +188,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM3 0 @@ -215,8 +214,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM4 0 @@ -241,8 +240,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM5 0 diff --git a/misc/config_tools/data/qemu/shared.xml b/misc/config_tools/data/qemu/shared.xml index 67dc43e15..f3e423b88 100644 --- a/misc/config_tools/data/qemu/shared.xml +++ b/misc/config_tools/data/qemu/shared.xml @@ -24,8 +24,7 @@ y n n - - + 0x2000 @@ -60,8 +59,8 @@ - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -90,8 +89,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 1 diff --git a/misc/config_tools/data/tgl-vecow-spc-7100-Corei7/hybrid.xml b/misc/config_tools/data/tgl-vecow-spc-7100-Corei7/hybrid.xml index 0f5f13d95..70d4afc9f 100644 --- a/misc/config_tools/data/tgl-vecow-spc-7100-Corei7/hybrid.xml +++ b/misc/config_tools/data/tgl-vecow-spc-7100-Corei7/hybrid.xml @@ -32,8 +32,7 @@ y n n - - + 0x2000 @@ -67,13 +66,12 @@ - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM SAFETY_VM0 3 - 0 @@ -102,17 +100,18 @@ INVALID_PCI_BASE - - - n n + + + + - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -149,8 +148,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 2 @@ -173,8 +172,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM2 2 diff --git a/misc/config_tools/data/tgl-vecow-spc-7100-Corei7/partitioned.xml b/misc/config_tools/data/tgl-vecow-spc-7100-Corei7/partitioned.xml index fe82a8ea4..c4cd2c073 100644 --- a/misc/config_tools/data/tgl-vecow-spc-7100-Corei7/partitioned.xml +++ b/misc/config_tools/data/tgl-vecow-spc-7100-Corei7/partitioned.xml @@ -32,8 +32,7 @@ y n n - - + 0x2000 @@ -67,8 +66,8 @@ - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM PRE_STD_VM0 0 @@ -104,18 +103,18 @@ INVALID_PCI_BASE + + n + 00:17.0 SATA controller: Intel Corporation Device a0d3 (rev 20) 00:1f.6 Ethernet controller: Intel Corporation Ethernet Connection (13) I219-LM (rev 20) - - n - - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM PRE_STD_VM1 1 @@ -139,7 +138,7 @@ YOCTO KERNEL_BZIMAGE Linux_bzImage - + rw rootwait root=/dev/sda3 console=ttyS0 noxsave nohpet no_timer_check ignore_loglevel log_buf_len=16M consoleblank=0 tsc=reliable reboot=acpi @@ -152,12 +151,12 @@ INVALID_PCI_BASE + + n + 00:14.0 USB controller: Intel Corporation Tiger Lake-LP USB 3.2 Gen 2x1 xHCI Host Controller (rev 20) - - n - diff --git a/misc/config_tools/data/tgl-vecow-spc-7100-Corei7/shared.xml b/misc/config_tools/data/tgl-vecow-spc-7100-Corei7/shared.xml index 7a9e6523b..ea22f11a0 100644 --- a/misc/config_tools/data/tgl-vecow-spc-7100-Corei7/shared.xml +++ b/misc/config_tools/data/tgl-vecow-spc-7100-Corei7/shared.xml @@ -32,8 +32,7 @@ y n n - - + 0x2000 @@ -79,8 +78,8 @@ - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -111,8 +110,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 0 @@ -126,6 +125,7 @@ 0 0 + PRIO_LOW VUART_LEGACY_PIO COM1_BASE @@ -135,11 +135,10 @@ INVALID_PCI_BASE n - PRIO_LOW - RTVM POST_LAUNCHED_VM + RTVM POST_RT_VM1 y @@ -154,6 +153,7 @@ 0 0 + PRIO_LOW VUART_LEGACY_PIO COM1_BASE @@ -163,6 +163,5 @@ INVALID_PCI_BASE n - PRIO_LOW diff --git a/misc/config_tools/data/whl-ipc-i5/hybrid.xml b/misc/config_tools/data/whl-ipc-i5/hybrid.xml index f8151365b..8308199ae 100644 --- a/misc/config_tools/data/whl-ipc-i5/hybrid.xml +++ b/misc/config_tools/data/whl-ipc-i5/hybrid.xml @@ -24,8 +24,7 @@ y n n - - + 0x2000 @@ -59,8 +58,8 @@ - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM SAFETY_VM0 3 @@ -93,16 +92,16 @@ INVALID_PCI_BASE - - - n + + + - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -138,8 +137,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 2 diff --git a/misc/config_tools/data/whl-ipc-i5/hybrid_rt.xml b/misc/config_tools/data/whl-ipc-i5/hybrid_rt.xml index 989aa4d3f..718e1e457 100644 --- a/misc/config_tools/data/whl-ipc-i5/hybrid_rt.xml +++ b/misc/config_tools/data/whl-ipc-i5/hybrid_rt.xml @@ -72,8 +72,8 @@ - RTVM PRE_LAUNCHED_VM + RTVM PRE_RT_VM0 y @@ -112,17 +112,17 @@ INVALID_PCI_BASE + + n + 00:17.0 SATA controller: Intel Corporation Device 9dd3 (rev 30) 03:00.0 Ethernet controller: Intel Corporation I210 Gigabit Network Connection (rev 03) - - n - - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -156,8 +156,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 0 @@ -182,8 +182,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM2 1 diff --git a/misc/config_tools/data/whl-ipc-i5/partitioned.xml b/misc/config_tools/data/whl-ipc-i5/partitioned.xml index 937dbdfa0..fe6456439 100644 --- a/misc/config_tools/data/whl-ipc-i5/partitioned.xml +++ b/misc/config_tools/data/whl-ipc-i5/partitioned.xml @@ -24,8 +24,7 @@ y n n - - + 0x2000 @@ -59,8 +58,8 @@ - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM PRE_STD_VM0 0 @@ -98,17 +97,17 @@ INVALID_PCI_BASE + + n + 00:17.0 SATA controller: Intel Corporation Cannon Point-LP SATA Controller [AHCI Mode] (rev 30) 04:00.0 Ethernet controller: Intel Corporation I210 Gigabit Network Connection (rev 03) - - n - - STANDARD_VM PRE_LAUNCHED_VM + STANDARD_VM PRE_STD_VM1 1 @@ -146,12 +145,12 @@ INVALID_PCI_BASE + + n + 00:14.0 USB controller: Intel Corporation Cannon Point-LP USB 3.1 xHCI Controller (rev 30) 03:00.0 Ethernet controller: Intel Corporation I210 Gigabit Network Connection (rev 03) - - n - diff --git a/misc/config_tools/data/whl-ipc-i5/sdc.xml b/misc/config_tools/data/whl-ipc-i5/sdc.xml index 3f174c7f4..b2981ea69 100644 --- a/misc/config_tools/data/whl-ipc-i5/sdc.xml +++ b/misc/config_tools/data/whl-ipc-i5/sdc.xml @@ -24,8 +24,7 @@ y n n - - + 0x2000 @@ -43,10 +42,11 @@ 0x00000010 + - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -75,8 +75,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 1 diff --git a/misc/config_tools/data/whl-ipc-i5/shared.xml b/misc/config_tools/data/whl-ipc-i5/shared.xml index 9ec8457bf..6a9e4362b 100644 --- a/misc/config_tools/data/whl-ipc-i5/shared.xml +++ b/misc/config_tools/data/whl-ipc-i5/shared.xml @@ -24,8 +24,7 @@ y n n - - + 0x2000 @@ -59,8 +58,8 @@ - STANDARD_VM SERVICE_VM + STANDARD_VM ACRN_Service_VM 0 @@ -90,8 +89,8 @@ - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM1 0 @@ -115,8 +114,8 @@ - RTVM POST_LAUNCHED_VM + RTVM POST_RT_VM1 2 @@ -141,8 +140,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM2 0 @@ -167,8 +166,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM3 0 @@ -193,8 +192,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM4 0 @@ -219,8 +218,8 @@ n - STANDARD_VM POST_LAUNCHED_VM + STANDARD_VM POST_STD_VM5 0 diff --git a/misc/config_tools/scenario_config/upgrader.py b/misc/config_tools/scenario_config/upgrader.py new file mode 100755 index 000000000..cdb1c0a4e --- /dev/null +++ b/misc/config_tools/scenario_config/upgrader.py @@ -0,0 +1,508 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 Intel Corporation. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import os +import argparse, logging +import re +from functools import lru_cache, partialmethod +from collections import defaultdict, namedtuple +import lxml.etree as etree + +from scenario_transformer import ScenarioTransformer + +from pipeline import PipelineObject, PipelineStage, PipelineEngine +from lxml_loader import LXMLLoadStage +from schema_slicer import SlicingSchemaByVMTypeStage + +class VirtualUartConnections: + class VirtualUartEndpoint: + # The BDF of PCI virtual UARTs starts from 00:10.0 + next_dev = defaultdict(lambda: 16) + + @classmethod + def from_endpoint_definition(cls, element): + # For v2.x style scenario XML, the name of the VM is a child of a `vm` node. + vm_name = element.xpath("ancestor::vm/name/text()").pop() + if "legacy" in element.tag: + base = element.find("base").text + io_port = \ + "0x3F8" if base.endswith("COM1_BASE") else \ + "0x2F8" if base.endswith("COM2_BASE") else \ + "0x3E8" if base.endswith("COM3_BASE") else \ + "0x2E8" if base.endswith("COM4_BASE") else \ + base + return cls(vm_name, io_port = io_port) + else: + dev = cls.next_dev[vm_name] + cls.next_dev[vm_name] += 1 + return cls(vm_name, pci_bdf = f"00:{dev:02x}.0") + + def __init__(self, vm_name, io_port = None, pci_bdf = None): + self.vm_name = vm_name + self.io_port = io_port + self.pci_bdf = pci_bdf + + class VirtualUartConnection: + next_id = 1 + + @classmethod + def from_connection_definition(cls, element): + name = element.find("name").text + ty = element.find("type").text + conn = cls(name = name, ty = ty) + for endpoint in element.findall("endpoint"): + vm_name_node = endpoint.find("vm_name") + vm_name = vm_name_node.text if vm_name_node is not None else "" + io_port_node = endpoint.find("io_port") + io_port = io_port_node.text if io_port_node is not None else None + vbdf_node = endpoint.find("vbdf") + vbdf = vbdf_node.text if vbdf_node is not None else None + conn.add_endpoint(VirtualUartConnections.VirtualUartEndpoint(vm_name, io_port, vbdf)) + return conn + + def __init__(self, name = None, ty = "legacy"): + if name: + self.name = name + else: + self.name = f"vUART connection {self.next_id}" + self.__class__.next_id += 1 + self.ty = ty + self.endpoints = [] + + def add_endpoint(self, endpoint): + self.endpoints.append(endpoint) + + def __init__(self): + self.conns = [] # List of connections + self.dangling_conns = {} # (vm_id, vuart_id) -> conn whose target is the key + + def add_endpoint(self, element): + """Parse the vUART endpoint definition in ACRN v2.x. Returns True if and only if the element is parsed properly.""" + + try: + key = (element.xpath("ancestor::vm/@id").pop(), element.xpath("@id").pop()) + if key in self.dangling_conns.keys(): + conn = self.dangling_conns.pop(key) + conn.add_endpoint(self.VirtualUartEndpoint.from_endpoint_definition(element)) + self.conns.append(conn) + else: + ty = "legacy" if "legacy" in element.tag else "pci" + conn = self.VirtualUartConnection(ty = ty) + conn.add_endpoint(self.VirtualUartEndpoint.from_endpoint_definition(element)) + self.dangling_conns[(element.xpath("target_vm_id/text()").pop(), element.xpath("target_uart_id/text()").pop())] = conn + + return True + except Exception as e: + # Skip vUART endpoint definition not satisfying the schema. The discarded-data warnings will report those + # unmigrated data. + logging.debug(e) + return False + + def add_connection(self, element): + """Parse the vUART connection definition in ACRN v3.x""" + self.conns.append(self.VirtualUartConnection.from_connection_definition(element)) + return True + + def format_xml_elements(self, xsd_element_node): + new_parent_node = etree.Element(xsd_element_node.get("name")) + for conn in self.conns: + new_node = etree.Element("vuart_connection") + etree.SubElement(new_node, "name").text = conn.name + etree.SubElement(new_node, "type").text = conn.ty + for endpoint in conn.endpoints: + new_endpoint_node = etree.SubElement(new_node, "endpoint") + etree.SubElement(new_endpoint_node, "vm_name").text = endpoint.vm_name + if endpoint.io_port: + etree.SubElement(new_endpoint_node, "io_port").text = endpoint.io_port + if endpoint.pci_bdf: + etree.SubElement(new_endpoint_node, "vbdf").text = endpoint.pci_bdf + new_parent_node.append(new_node) + return [new_parent_node] + +class SharedMemoryRegions: + class SharedMemoryRegion(namedtuple("SharedMemoryRegion", ["name", "size", "shared_vms"])): + # The BDF of IVSHMEM PCI functions starts from 00:08.0 + next_dev = defaultdict(lambda: 8) + + @classmethod + def from_hypervisor_encoding(cls, text, old_xml_etree): + parts = [p.strip() for p in text[text.find("/") + 1 :].split(",")] + name = parts[0] + size = parts[1] + shared_vm_ids = parts[2].split(":") + + shared_vms = [] + for vm_id in shared_vm_ids: + vm_name_node = old_xml_etree.xpath(f"//vm[@id='{vm_id}']/name") + if not vm_name_node: + logging.warning(f"VM {vm_id}, which is referred by shared memory region {name}, is not defined in the scenario.") + continue + + vm_name = vm_name_node[0].text + dev = cls.next_dev[vm_name] + cls.next_dev[vm_name] += 1 + shared_vms.append((vm_name, f"00:{dev:02x}.0")) + + return cls(name, size, shared_vms) + + @classmethod + def from_xml_node(cls, node): + name = node.get("name") + size = node.find("IVSHMEM_SIZE").text + shared_vms = [] + for shared_vm_node in node.find("IVSHMEM_VMS"): + vm_name = shared_vm_node.find("VM_NAME").text + vbdf = shared_vm_node.find("VBDF").text + shared_vms.append((vm_name, vbdf)) + return cls(name, size, shared_vms) + + def format_xml_element(self): + node = etree.Element("IVSHMEM_REGION") + node.set("name", self.name) + etree.SubElement(node, "IVSHMEM_SIZE").text = self.size + + vms_node = etree.SubElement(node, "IVSHMEM_VMS") + for vm_name, vbdf in self.shared_vms: + vm_node = etree.SubElement(vms_node, "IVSHMEM_VM") + etree.SubElement(vm_node, "VM_NAME").text = vm_name + etree.SubElement(vm_node, "VBDF").text = vbdf + + return node + + def __init__(self, old_xml_etree): + self.old_xml_etree = old_xml_etree + self.regions = {} + + def add_ivshmem_region(self, ivshmem_region_node): + """Parse IVSHMEM_REGION nodes in either v2.x and v3.x format.""" + + if len(ivshmem_region_node) == 0: + # ACRN v2.x format + region = self.SharedMemoryRegion.from_hypervisor_encoding(ivshmem_region_node.text, self.old_xml_etree) + self.regions[region.name] = region + else: + # ACRN v3.x format + region = self.SharedMemoryRegion.from_xml_node(ivshmem_region_node) + self.regions[region.name] = region + + def format_xml_element(self): + node = etree.Element("IVSHMEM") + for region in self.regions.values(): + node.append(region.format_xml_element()) + return node + +class ScenarioUpgrader(ScenarioTransformer): + @classmethod + def get_node(cls, element, xpath): + return next(iter(element.xpath(xpath, namespaces=cls.xpath_ns)), None) + + def __init__(self, xsd_etree, old_xml_etree): + super().__init__(xsd_etree, visit_optional_node=True) + self.old_xml_etree = old_xml_etree + + # Collect all nodes in old_xml_etree which will be used to track data not moved + self.old_data_nodes = set() + for node in old_xml_etree.iter(): + if node.text: + self.old_data_nodes.add(node) + + self.hv_vm_node_map = {} + + def get_from_old_data(self, new_parent_node, xpath): + hv_vm_node = new_parent_node + if hv_vm_node.tag not in ["vm", "hv"]: + hv_vm_node = next(new_parent_node.iterancestors(["vm", "hv"]), None) + old_hv_vm_node = self.hv_vm_node_map[hv_vm_node] + old_data_node = old_hv_vm_node.xpath(xpath) + return old_data_node + + def move_legacy_vuart(self, xsd_element_node, xml_parent_node, new_nodes): + # Preserve the legacy vuart for console only. + legacy_vuart = self.get_from_old_data(xml_parent_node, ".//legacy_vuart[@id = '0']") + if legacy_vuart: + new_nodes.append(legacy_vuart[0]) + for child in legacy_vuart[0].iter(): + self.old_data_nodes.discard(child) + return False + + def move_vuart_connections(self, xsd_element_node, xml_parent_node, new_nodes): + conns = VirtualUartConnections() + + # Fetch vUART endpoints in the old data + vuart_endpoints = self.old_xml_etree.xpath("//legacy_vuart[@id != '0' and base != 'INVALID_COM_BASE'] | //communication_vuart[base != 'INVALID_PCI_BASE']") + vuart_connections = self.old_xml_etree.xpath("//vuart_connection") + + for endpoint in vuart_endpoints: + if conns.add_endpoint(endpoint): + for child in endpoint.iter(): + self.old_data_nodes.discard(child) + + for connection in vuart_connections: + if conns.add_connection(connection): + for child in connection.iter(): + self.old_data_nodes.discard(child) + + new_nodes.extend(conns.format_xml_elements(xsd_element_node)) + + # Disconnected endpoints do not migrate, but remove such nodes from old_data_nodes to avoid raising + # data-is-discarded warnings. + for n in self.old_xml_etree.xpath("//legacy_vuart[@id != '0' and base = 'INVALID_COM_BASE'] | //communication_vuart[base = 'INVALID_PCI_BASE']"): + for child in n.iter(): + self.old_data_nodes.discard(child) + + return False + + def move_ivshmem(self, xsd_element_node, xml_parent_node, new_nodes): + regions = SharedMemoryRegions(self.old_xml_etree) + for old_region in self.old_xml_etree.xpath("//IVSHMEM_REGION"): + regions.add_ivshmem_region(old_region) + for child in old_region.iter(): + self.old_data_nodes.discard(child) + new_nodes.append(regions.format_xml_element()) + + return False + + def move_vm_type(self, xsd_element_node, xml_parent_node, new_nodes): + old_vm_type_node = self.get_from_old_data(xml_parent_node, ".//vm_type").pop() + old_guest_flag_nodes = self.get_from_old_data(xml_parent_node, ".//guest_flag[text() = 'GUEST_FLAG_RT']") + + new_node = etree.Element(xsd_element_node.get("name")) + if old_vm_type_node.text in ["PRE_RT_VM", "POST_RT_VM"] or old_guest_flag_nodes: + new_node.text = "RTVM" + elif old_vm_type_node.text in ["SAFETY_VM", "PRE_STD_VM", "POST_STD_VM"]: + new_node.text = "STANDARD_VM" + else: + new_node.text = old_vm_type_node.text + new_nodes.append(new_node) + self.old_data_nodes.discard(old_vm_type_node) + for n in old_guest_flag_nodes: + self.old_data_nodes.discard(n) + + return False + + def move_guest_flag(self, guest_flag, xsd_element_node, xml_parent_node, new_nodes): + old_data_nodes = self.get_from_old_data(xml_parent_node, f".//guest_flag[text() = '{guest_flag}']") + if old_data_nodes: + new_node = etree.Element(xsd_element_node.get("name")) + new_node.text = "y" + new_nodes.append(new_node) + for n in old_data_nodes: + self.old_data_nodes.discard(n) + else: + self.move_data_by_same_tag(xsd_element_node, xml_parent_node, new_nodes) + + return False + + def move_data_by_xpath(self, xpath, xsd_element_node, xml_parent_node, new_nodes): + element_tag = xsd_element_node.get("name") + + old_data_nodes = self.get_from_old_data(xml_parent_node, xpath) + if self.complex_type_of_element(xsd_element_node) is None: + max_occurs_raw = xsd_element_node.get("maxOccurs") + + # Use `len(old_data_nodes)` to ensure that all old data nodes are moved if an unbound number of + # occurrences is allowed. + max_occurs = \ + len(old_data_nodes) if max_occurs_raw == "unbounded" else \ + 1 if max_occurs_raw is None else \ + int(max_occurs_raw) + + if len(old_data_nodes) <= max_occurs: + for n in old_data_nodes: + new_node = etree.Element(element_tag) + new_node.text = n.text + for k, v in n.items(): + new_node.set(k, v) + new_nodes.append(new_node) + self.old_data_nodes.discard(n) + + return False + else: + # For each complex type containing multiple configuration items, this method can only create at most one + # single node, as there is no way for the default data movers to migrate multiple pieces of data of the same + # type to the new XML. + if old_data_nodes: + new_node = etree.Element(element_tag) + for k, v in old_data_nodes[0].items(): + new_node.set(k, v) + new_nodes.append(new_node) + return True + + def move_data_by_same_tag(self, xsd_element_node, xml_parent_node, new_nodes): + element_tag = xsd_element_node.get("name") + return self.move_data_by_xpath(f".//{element_tag}", xsd_element_node, xml_parent_node, new_nodes) + + def move_data_null(self, xsd_element_node, xml_parent_node, new_nodes): + return False + + data_movers = { + # Configuration items with the same name but under different parents + "vm/name": partialmethod(move_data_by_xpath, "./name"), + "os_config/name": partialmethod(move_data_by_xpath, ".//os_config/name"), + "epc_section/base": partialmethod(move_data_by_xpath, ".//epc_section/base"), + "console_vuart/base": partialmethod(move_data_by_xpath, ".//console_vuart/base"), + "epc_section/size": partialmethod(move_data_by_xpath, ".//epc_section/size"), + "memory/size": partialmethod(move_data_by_xpath, ".//memory/size"), + + # Guest flags + "lapic_passthrough": partialmethod(move_guest_flag, "GUEST_FLAG_LAPIC_PASSTHROUGH"), + "io_completion_polling": partialmethod(move_guest_flag, "GUEST_FLAG_IO_COMPLETION_POLLING"), + "nested_virtualization_support": partialmethod(move_guest_flag, "GUEST_FLAG_NVMX_ENABLED"), + "virtual_cat_support": partialmethod(move_guest_flag, "GUEST_FLAG_VCAT_ENABLED"), + "secure_world_support": partialmethod(move_guest_flag, "GUEST_FLAG_SECURITY_VM"), + "hide_mtrr_support": partialmethod(move_guest_flag, "GUEST_FLAG_HIDE_MTRR"), + "security_vm": partialmethod(move_guest_flag, "GUEST_FLAG_SECURITY_VM"), + + "legacy_vuart": move_legacy_vuart, + "vuart_connections": move_vuart_connections, + "IVSHMEM": move_ivshmem, + "vm_type": move_vm_type, + + "default": move_data_by_same_tag, + } + + def add_missing_nodes(self, xsd_element_node, xml_parent_node, xml_anchor_node): + new_nodes = [] + def call_mover(mover): + if isinstance(mover, partialmethod): + return mover.__get__(self, type(self))(xsd_element_node, xml_parent_node, new_nodes) + else: + return mover(self, xsd_element_node, xml_parent_node, new_nodes) + + # Common names (such as 'name' or 'base') may be used as tags in multiple places each of which has different + # meanings. In such cases it is ambiguious to query old data by that common tag alone. + element_tag = xsd_element_node.get("name") + element_tag_with_parent = f"{xml_parent_node.tag}/{element_tag}" + + mover_key = \ + element_tag_with_parent if element_tag_with_parent in self.data_movers.keys() else \ + element_tag if element_tag in self.data_movers.keys() else \ + "default" + visit_children = call_mover(self.data_movers[mover_key]) + + if xml_anchor_node is not None: + for n in new_nodes: + xml_anchor_node.addprevious(n) + else: + xml_parent_node.extend(new_nodes) + + if visit_children: + return new_nodes + else: + return [] + + @property + @lru_cache + def upgraded_etree(self): + new_xml_etree = etree.ElementTree(etree.Element(self.old_xml_etree.getroot().tag)) + root_node = new_xml_etree.getroot() + for k, v in self.old_xml_etree.getroot().items(): + new_xml_etree.getroot().set(k, v) + + # Migrate the HV and VM nodes, which are needed to kick off a thorough traversal of the existing scenario. + for old_node in self.old_xml_etree.getroot(): + new_node = etree.Element(old_node.tag) + + if old_node.tag == "vm": + # FIXME: Here we still hard code how the load order of a VM is specified in different versions of + # schemas. While it is not subject to frequent changes, it would be better if we use a more generic + # approach instead. + load_order_node = etree.SubElement(new_node, "load_order") + + # In the history we have two ways of specifying the load order of a VM: either by vm_type or by + # loader_order. + vm_type = old_node.xpath(".//vm_type/text()") + old_load_order_node = old_node.xpath(".//load_order") + if old_load_order_node: + load_order_node.text = old_load_order_node[0].text + self.old_data_nodes.discard(old_load_order_node[0]) + elif vm_type: + if vm_type[0].startswith("PRE_") or vm_type[0] in ["SAFETY_VM"]: + load_order_node.text = "PRE_LAUNCHED_VM" + elif vm_type[0].startswith("POST_"): + load_order_node.text = "POST_LAUNCHED_VM" + else: + load_order_node.text = "SERVICE_VM" + else: + logging.error(f"Cannot infer the loader order of VM {self.old_xml_etree.getelementpath(old_node)}") + continue + + root_node.append(new_node) + for k, v in old_node.items(): + new_node.set(k, v) + self.hv_vm_node_map[new_node] = old_node + + # Now fill the rest of configuration items using the old data + self.transform(new_xml_etree) + + return new_xml_etree + +class UpgradingScenarioStage(PipelineStage): + uses = {"schema_etree", "scenario_etree"} + provides = {"scenario_etree"} + + class DiscardedDataFilter(namedtuple("DiscardedDataFilter", ["path", "data", "info"])): + def filter(self, path, data): + simp_path = re.sub(r"\[[^\]]*\]", "", path) + if not simp_path.endswith(self.path): + return False + if self.data and data != self.data: + return False + + if self.info: + logging.info(f"{path} = '{data}': {self.info}") + return True + + filters = [ + DiscardedDataFilter("hv/FEATURES/IVSHMEM", None, "IVSHMEM is now automatically enabled if any IVSHMEM region is specified."), + DiscardedDataFilter("hv/FEATURES/NVMX_ENABLED", None, "Nest virtualization support is now automatically included if enabled for any VM."), + DiscardedDataFilter("hv/MISC_CFG/UEFI_OS_LOADER_NAME", None, None), + DiscardedDataFilter("vm/guest_flags/guest_flag", "0", None), + DiscardedDataFilter("vm/epc_section/base", "0", "Post-launched VMs cannot have EPC sections."), + DiscardedDataFilter("vm/epc_section/size", "0", "Post-launched VMs cannot have EPC sections."), + DiscardedDataFilter("vm/cpu_affinity/pcpu_id", None, "CPU affinity of the service VM is no longer needed."), + DiscardedDataFilter("vm/os_config/name", None, "Guest OS names are no longer needed in scenario definitions."), + ] + + def run(self, obj): + upgrader = ScenarioUpgrader(obj.get("schema_etree"), obj.get("scenario_etree")) + new_scenario_etree = upgrader.upgraded_etree + + discarded_data = [(obj.get("scenario_etree").getelementpath(n), n.text) for n in upgrader.old_data_nodes] + for path, data in sorted(discarded_data): + if not any(map(lambda x: x.filter(path, data), self.filters)): + escaped_data = data.replace("\n", "\\n") + logging.warning(f"{path} = '{escaped_data}' is discarded") + + obj.set("scenario_etree", new_scenario_etree) + +def main(args): + pipeline = PipelineEngine(["schema_path", "scenario_path"]) + pipeline.add_stages([ + LXMLLoadStage("schema"), + LXMLLoadStage("scenario"), + SlicingSchemaByVMTypeStage(), + UpgradingScenarioStage(), + ]) + + obj = PipelineObject(schema_path = args.schema, scenario_path = args.scenario) + pipeline.run(obj) + # We know we are using lxml to parse the scenario XML, so it is ok to use lxml specific write options here. + obj.get("scenario_etree").write(args.out, pretty_print=True) + +if __name__ == "__main__": + config_tools_dir = os.path.join(os.path.dirname(__file__), "..") + schema_dir = os.path.join(config_tools_dir, "schema") + + parser = argparse.ArgumentParser(description="Try adapting data in a scenario XML to the latest schema.") + parser.add_argument("scenario", help="Path to the scenario XML file from users") + parser.add_argument("out", nargs="?", default="out.xml", help="Path where the output is placed") + parser.add_argument("--schema", default=os.path.join(schema_dir, "config.xsd"), help="the XML schema that defines the syntax of scenario XMLs") + args = parser.parse_args() + + logging.basicConfig(level="INFO") + main(args)