acrn-config: add static allocators to rewrite scenario XMLs

In order to split the allocation logic from tranformation in the
configuration tool, this patch introduces the `static_allocators` directory
under `misc/acrn-config` to host scripts that statically allocate
resources (currently the physical memory) and fill the scenario XML with
the results. The logic is extracted from the existing configuration tool
which allocates resources when transforming XML files, while XPath is used
to read and manipulate the XML when possible.

The aim is to make transformation from XML files to C configuration sources
to be more trivial and easier to express in descriptive languages like
XSLT.

v2:
 * Instead of rewriting the scenario XML, the new version generates
   allocation results to a new XML named `allocation.xml` so that the form
   of the results are not restricted by the XML schema.

Tracked-On: #5644
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
Junjie Mao 2021-01-06 10:14:52 +08:00 committed by wenlingz
parent 9cff2bbdc0
commit 7a145253f3
3 changed files with 150 additions and 0 deletions

View File

@ -10,6 +10,7 @@ import shutil
import subprocess
import xml.etree.ElementTree as ET
import re
import lxml
ACRN_CONFIG_TARGET = ''
@ -183,6 +184,39 @@ def get_xml_attrib(config_file, attrib):
return (err_dic, value)
def count_nodes(xpath, etree):
return int(etree.xpath(f"count({xpath})"))
def get_text(xpath, etree):
result = etree.xpath(f"{xpath}/text()")
assert len(result) == 1, "Internal error: cannot get texts from multiple nodes at a time"
return result[0]
def update_text(xpath, value, etree, overwrite=False):
result = etree.xpath(f"{xpath}")
assert len(result) == 1, "Internal error: cannot set text to multiple nodes at a time"
if overwrite or not result[0].text:
result[0].text = str(value)
def append_node(xpath, value, etree):
# Look for an existing ancestor node
parts = xpath.split("/")
ancestor_level = 1
ancestor = None
while ancestor_level < len(parts):
result = etree.xpath("/".join(parts[:-ancestor_level]))
assert len(result) <= 1, "Internal error: cannot append element nodes to multiple ancestors"
if len(result) == 1:
ancestor = result[0]
break
ancestor_level += 1
assert ancestor is not None, f"Internal error: cannot find an existing ancestor for {xpath}"
for tag in parts[-ancestor_level:]:
child = lxml.etree.Element(tag)
ancestor.append(child)
ancestor = child
child.text = str(value)
def get_board_name():
"""

View File

@ -0,0 +1,75 @@
#!/usr/bin/env python3
#
# Copyright (C) 2021 Intel Corporation. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
import sys, os
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'library'))
import common, board_cfg_lib
VM_NUM_MAP_TOTAL_HV_RAM_SIZE = {
# 120M
2:0x7800000,
# 150M
3:0x9600000,
# 190M
4:0xBE00000,
# 210M
5:0xD200000,
# 250M
6:0xFA00000,
# 300M
7:0x12C00000,
# 328M
8:0x14800000,
}
HV_RAM_SIZE_MAX = 0x40000000
MEM_ALIGN = 2 * common.SIZE_M
def fn(board_etree, scenario_etree, allocation_etree):
# this dictonary mapped with 'address start':'mem range'
ram_range = {}
vm_count = common.count_nodes("//*[local-name() = 'vm']", scenario_etree)
hv_ram_size = VM_NUM_MAP_TOTAL_HV_RAM_SIZE[vm_count]
ivshmem_enabled = common.get_text("//IVSHMEM_ENABLED", scenario_etree)
total_shm_size = 0
if ivshmem_enabled == 'y':
raw_shmem_regions = scenario_etree.xpath("//IVSHMEM_REGION/text()")
for raw_shm in raw_shmem_regions:
if raw_shm.strip() == '':
continue
raw_shm_splited = raw_shm.split(',')
if len(raw_shm_splited) == 3 and raw_shm_splited[0].strip() != '' \
and raw_shm_splited[1].strip() != '' and len(raw_shm_splited[2].strip().split(':')) >= 1:
try:
size = raw_shm_splited[1].strip()
int_size = int(size) * 0x100000
total_shm_size += int_size
except Exception as e:
print(e)
hv_ram_size += total_shm_size
assert(hv_ram_size <= HV_RAM_SIZE_MAX)
# reseve 16M memory for hv sbuf, ramoops, etc.
reserved_ram = 0x1000000
# We recommend to put hv ram start address high than 0x10000000 to
# reduce memory conflict with GRUB/SOS Kernel.
hv_start_offset = 0x10000000
total_size = reserved_ram + hv_ram_size
for start_addr in list(board_cfg_lib.USED_RAM_RANGE):
if hv_start_offset <= start_addr < 0x80000000:
del board_cfg_lib.USED_RAM_RANGE[start_addr]
ram_range = board_cfg_lib.get_ram_range()
avl_start_addr = board_cfg_lib.find_avl_memory(ram_range, str(total_size), hv_start_offset)
hv_start_addr = int(avl_start_addr, 16) + int(hex(reserved_ram), 16)
hv_start_addr = common.round_up(hv_start_addr, MEM_ALIGN)
board_cfg_lib.USED_RAM_RANGE[hv_start_addr] = total_size
common.append_node("/acrn-config/hv/MEMORY/HV_RAM_START", hex(hv_start_addr), allocation_etree)
common.append_node("/acrn-config/hv/MEMORY/HV_RAM_SIZE", hex(hv_ram_size), allocation_etree)

View File

@ -0,0 +1,41 @@
#!/usr/bin/env python3
#
# Copyright (C) 2021 Intel Corporation. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
import sys, os
import lxml.etree
import argparse
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'library'))
import common
from importlib import import_module
def main(args):
# Initialize configuration libraries for backward compatibility
common.BOARD_INFO_FILE = args.board
common.SCENARIO_INFO_FILE = args.scenario
common.get_vm_num(args.scenario)
common.get_vm_types()
scripts_path = os.path.dirname(os.path.realpath(__file__))
current = os.path.basename(__file__)
board_etree = lxml.etree.parse(args.board)
scenario_etree = lxml.etree.parse(args.scenario)
allocation_etree = lxml.etree.ElementTree(element=lxml.etree.fromstring("<acrn-config></acrn-config>"))
for script in [f for f in os.listdir(scripts_path) if f.endswith(".py") and f != current]:
module_name = os.path.splitext(script)[0]
module = import_module(f"{module_name}")
module.fn(board_etree, scenario_etree, allocation_etree)
allocation_etree.write(args.output, pretty_print=True)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("--board", help="the XML file summarizing characteristics of the target board")
parser.add_argument("--scenario", help="the XML file specifying the scenario to be set up")
parser.add_argument("--output", help="location of the output XML")
args = parser.parse_args()
main(args)