config-tools: rewrite the generator of launch scripts

The launch script generator today (and the scripts that it generates) is
fundamentally built on the concept of PCI device classes, with the
restriction that at most one PCI function per class can be passed through
to a post-launched VM. This has put inproper constraint on the scenarios
users can set up, especially on server platforms or those with SR-IOV
capable devices.

As it is too tedious to change such deep-rooted concept, this patch
rewrites the launch script generator and refines the structure of the
generated scripts so that PCI functions are identified only by their
BDF.

This change serves as a mandatory step to align the way how passthrough
devices are configured for pre-launched and post-launched VMs, which
eventually allows us to present a unified view in the configurator for
assigning passthrough device.

v2 -> v3:
 * Rename sos_id to service_vm_id and user_vmid to user_vm_id.
 * Refine a couple of info messages in the launch script template.

v1 -> v2:
 * Fix wording issues identified during review.
 * Exit when the out_dir is an existing regular file.

Tracked-On: #6690
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
Junjie Mao 2022-02-15 21:09:54 +08:00 committed by acrnsi-robot
parent eb9a58c70e
commit 2450b4c77f
2 changed files with 480 additions and 218 deletions

533
misc/config_tools/launch_config/launch_cfg_gen.py Normal file → Executable file
View File

@ -1,269 +1,366 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
#!/usr/bin/env python3
#
# Copyright (C) 2022 Intel Corporation.
#
# SPDX-License-Identifier: BSD-3-Clause
#
import sys, os
import argparse
import lxml.etree as etree
import logging
import os
import re
import sys
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'library'))
from launch_item import AvailablePthru, PthruSelected, VirtioDeviceSelect, AcrnDmArgs, SriovDeviceInput
import board_cfg_lib
import launch_cfg_lib
import com
import common
import copy
ACRN_PATH = common.SOURCE_ROOT_DIR
ACRN_CONFIG_DEF = ACRN_PATH + '/misc/config_tools/data/'
def eval_xpath(element, xpath, default_value=None):
return next(iter(element.xpath(xpath)), default_value)
class LaunchScript:
script_template_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "launch_script_template.sh")
def get_launch_item_values(board_info, scenario_info=None):
"""
Get items which capable multi select for user
:param board_info: it is a file what contains board information for script to read from
:param sceanrio_info: it is a file what contains scenario information for script to read from
"""
common.BOARD_INFO_FILE = board_info
launch_item_values = {}
class VirtualBDFAllocator:
def __init__(self):
# Reserved slots:
# 0 - For (virtual) hostbridge
# 1 - For (virtual) LPC
# 2 - For passthrough integarted GPU (either PF or VF)
# 31 - For LPC bridge needed by integrated GPU
self._free_slots = list(range(3, 30))
# passthrough devices
pthru = AvailablePthru(board_info)
pthru.get_pci_dev()
pthru.insert_nun()
def get_virtual_bdf(self, device_etree = None, options = None):
if device_etree is not None:
bus = eval_xpath(device_etree, "../@address")
vendor_id = eval_xpath(device_etree, "vendor/text()")
class_code = eval_xpath(device_etree, "class/text()")
# pre passthrough device for ui
launch_item_values["user_vm,passthrough_devices,usb_xdci"] = pthru.avl["usb_xdci"]
launch_item_values["user_vm,passthrough_devices,gpu"] = pthru.avl["gpu"]
launch_item_values["user_vm,passthrough_devices,ipu"] = pthru.avl["ipu"]
launch_item_values["user_vm,passthrough_devices,ipu_i2c"] = pthru.avl["ipu_i2c"]
launch_item_values["user_vm,passthrough_devices,cse"] = pthru.avl["cse"]
launch_item_values["user_vm,passthrough_devices,audio"] = pthru.avl["audio"]
launch_item_values["user_vm,passthrough_devices,audio_codec"] = pthru.avl["audio_codec"]
launch_item_values["user_vm,passthrough_devices,sd_card"] = pthru.avl["sd_card"]
launch_item_values["user_vm,passthrough_devices,wifi"] = pthru.avl["wifi"]
launch_item_values["user_vm,passthrough_devices,ethernet"] = pthru.avl["ethernet"]
launch_item_values["user_vm,passthrough_devices,sata"] = pthru.avl["sata"]
launch_item_values["user_vm,passthrough_devices,nvme"] = pthru.avl["nvme"]
launch_item_values["user_vm,passthrough_devices,bluetooth"] = pthru.avl["bluetooth"]
# VGA-compatible controller, either integrated or discrete GPU
if class_code == "0x030000":
return 2
# acrn dm available optargs
launch_item_values['user_vm,user_vm_type'] = launch_cfg_lib.USER_VM_TYPES
launch_item_values["user_vm,rtos_type"] = launch_cfg_lib.RTOS_TYPE
if options:
if "igd" in options:
return 2
launch_item_values["user_vm,vbootloader"] = launch_cfg_lib.BOOT_TYPE
launch_item_values['user_vm,vuart0'] = launch_cfg_lib.DM_VUART0
launch_item_values["user_vm,cpu_affinity"] = board_cfg_lib.get_processor_info()
launch_item_values['user_vm,enable_ptm'] = launch_cfg_lib.y_n
launch_cfg_lib.set_shm_regions(launch_item_values, scenario_info)
launch_cfg_lib.set_pci_vuarts(launch_item_values, scenario_info)
next_vbdf = self._free_slots.pop(0)
return next_vbdf
return launch_item_values
class PassThruDeviceOptions:
passthru_device_options = {
# "0x0200": ["enable_ptm"], # Ethernet controller, added if PTM is enabled for the VM
}
def _add_option(self, class_code, option):
current_option = self._options.setdefault(class_code, [])
self._options[class_code] = current_option.append("enable_ptm")
def validate_launch_setting(board_info, scenario_info, launch_info):
"""
This is validate the data setting from scenario xml
:param board_info: it is a file what contains board information for script to read from
:param scenario_info: it is a file what user have already setting to
:return: return a dictionary contain errors
"""
common.SCENARIO_INFO_FILE = scenario_info
common.get_vm_types()
def __init__(self, vm_launch_etree):
self._options = copy.copy(self.passthru_device_options)
if eval_xpath(vm_launch_etree, ".//enable_ptm/text()") == "y":
self._add_option("0x0200", "enable_ptm")
launch_cfg_lib.ERR_LIST = {}
common.BOARD_INFO_FILE = board_info
common.SCENARIO_INFO_FILE = scenario_info
common.LAUNCH_INFO_FILE = launch_info
def get_option(self, device_etree):
passthru_options = []
if device_etree is not None:
class_code = eval_xpath(device_etree, "class/text()", "")
for k,v in self._options.items():
if class_code.startswith(k):
passthru_options.extend(v)
return ",".join(passthru_options)
# init available pt devices and get selected pt devices
pt_avl = AvailablePthru(board_info)
pt_sel = PthruSelected(launch_info, pt_avl.bdf_desc_map, pt_avl.bdf_vpid_map)
pt_sel.get_bdf()
pt_sel.get_vpid()
pt_sel.get_slot()
pt_sel.check_item()
def __init__(self, board_etree, vm_name, vm_launch_etree):
self._board_etree = board_etree
self._vm_launch_etree = vm_launch_etree
# virt-io devices
virtio = VirtioDeviceSelect(launch_info)
virtio.get_virtio()
virtio.check_virtio()
self._vm_name = vm_name
self._vm_descriptors = {}
self._init_commands = []
self._dm_parameters = []
self._deinit_commands = []
sriov = SriovDeviceInput(launch_info)
sriov.get_sriov()
sriov.check_sriov(pt_sel)
self._vbdf_allocator = self.VirtualBDFAllocator()
self._passthru_options = self.PassThruDeviceOptions(vm_launch_etree)
# acrn dm arguments
dm = AcrnDmArgs(board_info, scenario_info, launch_info)
dm.get_args()
dm.check_item()
def add_vm_descriptor(self, name, value):
self._vm_descriptors[name] = value
return (launch_cfg_lib.ERR_LIST, pt_sel, virtio, dm, sriov)
def add_init_command(self, command):
if command not in self._init_commands:
self._init_commands.append(command)
def add_deinit_command(self, command):
if command not in self._deinit_commands:
self._deinit_commands.append(command)
def ui_entry_api(board_info, scenario_info, launch_info, out=''):
def add_plain_dm_parameter(self, opt):
full_opt = f"\"{opt}\""
if full_opt not in self._dm_parameters:
self._dm_parameters.append(full_opt)
err_dic = {}
arg_list = ['launch_cfg_gen.py', '--board', board_info, '--scenario', scenario_info, '--launch', launch_info, '--user_vmid', '0', '--out', out]
def add_dynamic_dm_parameter(self, cmd, opt=""):
full_cmd = f"{cmd:40s} {opt}".strip()
full_opt = f"`{full_cmd}`"
if full_opt not in self._dm_parameters:
self._dm_parameters.append(full_opt)
err_dic = common.prepare()
if err_dic:
return err_dic
def to_string(self):
s = ""
err_dic = main(arg_list)
launch_cfg_lib.reset_pt_slot()
return err_dic
with open(self.script_template_path, "r") as f:
s += f.read()
s += """
###
# The followings are generated by launch_cfg_gen.py
###
"""
s += "\n"
def get_names():
s += "# Defining variables that describe VM types\n"
for name, value in self._vm_descriptors.items():
s += f"{name}={value}\n"
s += "\n"
names = {}
s += "# Initializing\n"
for command in self._init_commands:
s += f"{command}\n"
s += "\n"
# get User Vm name
user_vm_types = launch_cfg_lib.get_user_vm_type()
names['user_vm_types'] = user_vm_types
s += "# Invoking ACRN device model\n"
s += "dm_params=(\n"
for param in self._dm_parameters:
s += f" {param}\n"
s += ")\n\n"
# get user_vm name
user_vm_names = launch_cfg_lib.get_user_vm_names()
names['user_vm_names'] = user_vm_names
s += "echo \"Launch device model with parameters: ${dm_params[*]}\"\n"
s += "acrn-dm ${dm_params[*]}\n\n"
# get board name
(err_dic, board_name) = common.get_board_name()
if err_dic:
return (err_dic, names)
names['board_name'] = board_name
s += "# Deinitializing\n"
for command in self._deinit_commands:
s += f"{command}\n"
# get scenario name
(err_dic, scenario_name) = common.get_scenario_name()
if err_dic:
return (err_dic, names)
names['scenario_name'] = scenario_name
return s
return (err_dic, names)
def write_to_file(self, path):
with open(path, "w") as f:
f.write(self.to_string())
logging.info(f"Successfully generated launch script {path} for VM '{self._vm_name}'.")
def add_virtual_device(self, kind, vbdf=None, options=""):
if "virtio" in kind and eval_xpath(self._vm_launch_etree, ".//rtos_type/text()", "no") != "no":
self.add_plain_dm_parameter("--virtio_poll 1000000")
def generate_script_file(names, pt_sel, virt_io, dm, sriov, vmid, config):
if vbdf is None:
vbdf = self._vbdf_allocator.get_virtual_bdf()
self.add_dynamic_dm_parameter("add_virtual_device", f"{vbdf} {kind} {options}")
user_vm_type = names['user_vm_types'][vmid]
board_name = names['board_name']
scenario_name = names['scenario_name']
def add_passthru_device(self, bus, dev, fun, options=""):
device_etree = eval_xpath(self._board_etree, f"//bus[@type='pci' and @address='0x{bus:x}']/device[@address='0x{(dev << 16) | fun:x}']")
if not options:
options = self._passthru_options.get_option(device_etree)
header_info = "#!/bin/bash\n" +\
"# board: {}, scenario: {}, user_vm: {}".format(
board_name.upper(), scenario_name.upper(), user_vm_type.upper())
vbdf = self._vbdf_allocator.get_virtual_bdf(device_etree, options)
self.add_dynamic_dm_parameter("add_passthrough_device", f"{vbdf} 0000:{bus:02x}:{dev:02x}.{fun} {options}")
print("{}".format(header_info), file=config)
com.gen(names, pt_sel, virt_io, dm, sriov, vmid, config)
if launch_cfg_lib.ERR_LIST:
return launch_cfg_lib.ERR_LIST
# Enable interrupt storm monitoring if the VM has any passthrough device other than the integrated GPU (whose
# vBDF is fixed to 2)
if vbdf != 2:
self.add_dynamic_dm_parameter("add_interrupt_storm_monitor", "10000 10 1 100")
def has_dm_parameter(self, fn):
try:
next(filter(fn, self._dm_parameters))
return True
except StopIteration:
return False
def main(args):
"""
This is main function to start generate launch script
:param args: it is a command line args for the script
"""
# get parameters
(err_dic, board_info_file, scenario_info_file, launch_info_file, vm_th, output_folder) = launch_cfg_lib.get_param(args)
if err_dic:
return err_dic
def cpu_id_to_lapic_id(board_etree, vm_name, cpus):
ret = []
# check env
err_dic = common.prepare()
if err_dic:
return err_dic
# vm_th =[0..post_vm_max]
# 0: generate all launch script for all post vm launch script
# 1: generate launch script for 1st post vm launch script
# 2: generate launch script for 2nd post vm launch script
common.BOARD_INFO_FILE = board_info_file
common.SCENARIO_INFO_FILE = scenario_info_file
common.LAUNCH_INFO_FILE = launch_info_file
common.get_vm_types()
# get post vm dic
post_num_list = launch_cfg_lib.get_post_num_list()
# get toatl post vm number and total vm in launch config file
(launch_vm_count, post_vm_count) = launch_cfg_lib.get_post_vm_cnt()
if vm_th < 0 or vm_th > post_vm_count:
err_dic['user_vmid err:'] = "--user_vmid shoudl be positive and less than total post vm count in scenario"
if vm_th and vm_th not in post_num_list:
err_dic['user_vmid err:'] = "--user_vmid generate the {} post vm, but this vm's config not in launch xml".format(vm_th)
# validate vm_names
scenario_names = common.get_leaf_tag_map(scenario_info_file, "name").values()
for user_vm_id, vm_name in launch_cfg_lib.get_user_vm_names().items():
if not re.match(r"^\S{1,15}$", vm_name):
err_name = 'user_vm id="{}" name error:'.format(user_vm_id)
err_dic[err_name] = 'vm_name only allowed 1-15 characters with letters, numbers & symbols ' \
'(not include space)'
if vm_name not in scenario_names:
logging.warning(
'user_vm id="{}"\'s vm_name ({}) not found in scenario file, set it to dynamic vm.'.format(
user_vm_id, vm_name
)
)
if err_dic:
return err_dic
# validate launch config file
(err_dic, pt_sel, virt_io, dm, sriov) = validate_launch_setting(board_info_file, scenario_info_file, launch_info_file)
if err_dic:
return err_dic
# check if this is the scenario config which matched board info
(err_dic, status) = launch_cfg_lib.is_config_file_match()
if not status:
return err_dic
(err_dic, names) = get_names()
if err_dic:
return err_dic
# create output directory
board_name = names['board_name']
if output_folder:
if os.path.isabs(output_folder):
output = os.path.join(output_folder, board_name, 'output')
for cpu in cpus:
lapic_id = eval_xpath(board_etree, f"//processors//thread[cpu_id='{cpu}']/apic_id/text()", None)
if lapic_id is not None:
ret.append(int(lapic_id, 16))
else:
output = os.path.join(ACRN_PATH, output_folder, board_name, 'output')
logging.warning(f"CPU {cpu} is not defined in the board XML, so it can't be available to VM {vm_name}")
return ret
def generate_for_one_vm(board_etree, vm_scenario_etree, vm_launch_etree, vm_id):
vm_name = eval_xpath(vm_launch_etree, ".//vm_name/text()", f"ACRN Post-Launched VM")
script = LaunchScript(board_etree, vm_name, vm_launch_etree)
script.add_init_command("probe_modules")
###
# VM types and guest OSes
###
if eval_xpath(vm_launch_etree, ".//user_vm_type/text()") == "WINDOWS":
script.add_plain_dm_parameter("--windows")
script.add_vm_descriptor("rtos_type", f"'{eval_xpath(vm_launch_etree, './/rtos_type/text()', 'no')}'")
###
# CPU and memory resources
###
cpus_in_launch_xml = set(vm_launch_etree.xpath(".//cpu_affinity/pcpu_id[text() != '']/text()"))
cpus_in_scenario_xml = set(vm_scenario_etree.xpath(".//cpu_affinity/pcpu_id[text() != '']/text()"))
if cpus_in_launch_xml:
cpus = cpus_in_scenario_xml & cpus_in_launch_xml
if not cpus:
logging.error(f"CPUs assigned to VM '{vm_name}' in the launch XML are outside of those allowed by the scenario XML.")
else:
output = os.path.join(ACRN_CONFIG_DEF, board_name, 'output')
output = os.path.abspath(output)
common.mkdir(output)
cpus = cpus_in_scenario_xml
if not cpus:
logging.error(f"VM '{vm_name}' has no CPU assigned in either the scenario or the launch XML.")
lapic_ids = cpu_id_to_lapic_id(board_etree, vm_name, cpus)
if lapic_ids:
script.add_dynamic_dm_parameter("add_cpus", f"{' '.join([str(x) for x in sorted(lapic_ids)])}")
# generate launch script
if vm_th:
script_name = "launch_user_vm_id{}.sh".format(vm_th)
launch_script_file = os.path.join(output, script_name)
with open(launch_script_file, mode = 'w', newline=None, encoding='utf-8') as config:
err_dic = generate_script_file(names, pt_sel, virt_io.dev, dm.args, sriov.dev, vm_th, config)
if err_dic:
return err_dic
script.add_plain_dm_parameter(f"-m {eval_xpath(vm_launch_etree, './/mem_size/text()')}M")
if eval_xpath(vm_scenario_etree, "//SSRAM_ENABLED") == "y" and \
eval_xpath(vm_launch_etree, ".//user_vm_type/text()") == "PREEMPT-RT LINUX":
script.add_plain_dm_parameter("--ssram")
###
# Guest BIOS
###
if eval_xpath(vm_launch_etree, ".//vbootloader/text()") == "ovmf":
script.add_plain_dm_parameter("--ovmf /usr/share/acrn/bios/OVMF.fd")
###
# Devices
###
# Emulated platform devices
if eval_xpath(vm_launch_etree, ".//user_vm_type/text()") != "PREEMPT-RT LINUX":
script.add_virtual_device("lpc", vbdf="1:0")
if eval_xpath(vm_launch_etree, ".//vuart0/text()") == "Enable":
script.add_plain_dm_parameter("-l com1,stdio")
# Emulated PCI devices
script.add_virtual_device("hostbridge", vbdf="0:0")
if eval_xpath(vm_scenario_etree, "//IVSHMEM_ENABLED/text()") == "y":
for ivshmem in vm_launch_etree.xpath("//shm_region[text() != '']/text()"):
script.add_virtual_device("ivshmem", options=ivshmem)
if eval_xpath(vm_launch_etree, ".//console_vuart/text()") == "Enable":
script.add_virtual_device("uart", options="vuart_idx:0")
for comm_vuart in vm_launch_etree.xpath(".//communication_vuart/@id"):
script.add_virtual_device("uart", options=f"vuart_idx:{comm_vuart}")
# Mediated PCI devices, including virtio
for usb_xhci in vm_launch_etree.xpath(".//usb_xhci[text() != '']/text()"):
script.add_virtual_device("xhci", options=usb_xhci)
for virtio_input in vm_launch_etree.xpath(".//virtio_devices/input[text() != '']/text()"):
script.add_virtual_device("virtio-input", options=virtio_input)
for virtio_console in vm_launch_etree.xpath(".//virtio_devices/console[text() != '']/text()"):
script.add_virtual_device("virtio-console", options=virtio_console)
for virtio_network in vm_launch_etree.xpath(".//virtio_devices/network[text() != '']/text()"):
params = virtio_network.split(",", maxsplit=1)
tap_conf = f"tap={params[0]}"
params = [tap_conf] + params[1:]
script.add_init_command(f"mac=$(cat /sys/class/net/e*/address)")
params.append(f"mac_seed=${{mac:0:17}}-{vm_name}")
script.add_virtual_device("virtio-net", options=",".join(params))
for virtio_block in vm_launch_etree.xpath(".//virtio_devices/block[text() != '']/text()"):
params = virtio_block.split(":", maxsplit=1)
if len(params) == 1:
script.add_virtual_device("virtio-blk", options=virtio_block)
else:
block_device = params[0]
rootfs_img = params[1]
var = f"dir_{os.path.basename(block_device)}"
script.add_init_command(f"{var}=`mount_partition {block_device}`")
script.add_virtual_device("virtio-blk", options=os.path.join(f"${{{var}}}", rootfs_img))
script.add_deinit_command(f"unmount_partition ${{{var}}}")
# Passthrough PCI devices
bdf_regex = re.compile("([0-9a-f]{2}):([0-1][0-9a-f]).([0-7])")
for passthru_device in vm_launch_etree.xpath(".//passthrough_devices/*/text()"):
m = bdf_regex.match(passthru_device)
if not m:
continue
script.add_passthru_device(int(m.group(1), 16), int(m.group(2), 16), int(m.group(3), 16))
for sriov_gpu_device in vm_launch_etree.xpath(".//sriov/gpu/text()"):
m = bdf_regex.match(sriov_gpu_device)
if not m:
continue
script.add_passthru_device(int(m.group(1), 16), int(m.group(2), 16), int(m.group(3), 16), options="igd-vf")
###
# Miscellaneous
###
script.add_dynamic_dm_parameter("add_rtvm_options")
script.add_dynamic_dm_parameter("add_logger_settings", "console=4 kmsg=3 disk=5")
###
# Lastly, conclude the device model parameters with the VM name
###
script.add_plain_dm_parameter(f"{vm_name}")
return script
def main(board_xml, scenario_xml, launch_xml, user_vm_id, out_dir):
board_etree = etree.parse(board_xml)
scenario_etree = etree.parse(scenario_xml)
launch_etree = etree.parse(launch_xml)
service_vm_id = eval_xpath(scenario_etree, "//vm[vm_type='SERVICE_VM']/@id")
post_vms = scenario_etree.xpath("//vm[starts-with(vm_type, 'POST_')]")
if service_vm_id is None and len(post_vms) > 0:
logging.error("The scenario does not define a service VM so no launch scripts will be generated for the post-launched VMs in the scenario.")
return 1
service_vm_id = int(service_vm_id)
try:
os.mkdir(out_dir)
except FileExistsError:
if os.path.isfile(out_dir):
logging.error(f"Cannot create output directory {out_dir}: File exists")
return 1
except Exception as e:
logging.error(f"Cannot create output directory: {e}")
return 1
if user_vm_id == 0:
post_vm_ids = [int(vm_scenario_etree.get("id")) - service_vm_id for vm_scenario_etree in post_vms]
else:
for post_vm_i in post_num_list:
script_name = "launch_user_vm_id{}.sh".format(post_vm_i)
launch_script_file = os.path.join(output, script_name)
with open(launch_script_file, mode = 'w', newline='\n', encoding='utf-8') as config:
err_dic = generate_script_file(names, pt_sel, virt_io.dev, dm.args, sriov.dev, post_vm_i, config)
if err_dic:
return err_dic
post_vm_ids = [user_vm_id]
if not err_dic:
print("Launch files in {} is generated successfully!".format(output))
else:
print("Launch files generate failed".format(output))
for post_vm_id in post_vm_ids:
vm_scenario_etree = eval_xpath(scenario_etree, f"//vm[@id = {service_vm_id + post_vm_id}]")
vm_launch_etree = eval_xpath(launch_etree, f"//user_vm[@id='{post_vm_id}']")
if vm_scenario_etree is None:
logging.warning(f"Post-launched VM {post_vm_id} is not specified in the scenario XML, so no launch script will be generated.")
continue
return err_dic
if vm_launch_etree is None:
logging.warning(f"Post-launched VM {post_vm_id} is not specified in the launch XML, so no launch script will be generated.")
continue
script = generate_for_one_vm(board_etree, vm_scenario_etree, vm_launch_etree, post_vm_id)
script.write_to_file(os.path.join(out_dir, f"launch_user_vm_id{post_vm_id}.sh"))
if __name__ == '__main__':
return 0
ARGS = sys.argv
err_dic = main(ARGS)
if err_dic:
for err_k, err_v in err_dic.items():
common.print_red("{}: {}".format(err_k, err_v), err=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("--launch", help="the XML file specifying the parameters of post-launched VMs")
parser.add_argument("--user_vmid", type=int, default=0, help="the post-launched VM ID (as is specified in the launch XML) whose launch script is to be generated, or 0 if all post-launched VMs shall be processed")
parser.add_argument("--out", default="output", help="path to the directory where generated scripts are placed")
args = parser.parse_args()
logging.basicConfig(level="INFO")
sys.exit(main(args.board, args.scenario, args.launch, args.user_vmid, args.out))

View File

@ -0,0 +1,165 @@
#!/bin/bash
#
# Copyright (C) 2022 Intel Corporation.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# Helper functions
function probe_modules() {
modprobe pci_stub
}
function offline_cpus() {
# Each parameter of this function is considered the APIC ID (as is reported in /proc/cpuinfo, in decimal) of a CPU
# assigned to a post-launched RTVM.
for i in $*; do
processor_id=$(grep -B 15 "apicid.*: ${i}$" /proc/cpuinfo | grep "^processor" | head -n 1 | cut -d ' ' -f 2)
if [ -z ${processor_id} ]; then
continue
fi
cpu_path="/sys/devices/system/cpu/cpu${processor_id}"
if [ -f ${cpu_path}/online ]; then
online=`cat ${cpu_path}/online`
echo cpu${processor_id} online=${online} >> /dev/stderr
if [ "${online}" = "1" ] && [ "${processor_id}" != "0" ]; then
echo 0 > ${cpu_path}/online
online=`cat ${cpu_path}/online`
# during boot time, cpu hotplug may be disabled by pci_device_probe during a pci module insmod
while [ "${online}" = "1" ]; do
sleep 1
echo 0 > ${cpu_path}/online
online=`cat ${cpu_path}/online`
done
echo ${processor_id} > /sys/devices/virtual/misc/acrn_hsm/remove_cpu
fi
fi
done
}
function unbind_device() {
physical_bdf=$1
vendor_id=$(cat /sys/bus/pci/devices/${physical_bdf}/vendor)
device_id=$(cat /sys/bus/pci/devices/${physical_bdf}/device)
echo $(printf "%04x %04x" ${vendor_id} ${device_id}) > /sys/bus/pci/drivers/pci-stub/new_id
echo ${physical_bdf} > /sys/bus/pci/devices/${physical_bdf}/driver/unbind
echo ${physical_bdf} > /sys/bus/pci/drivers/pci-stub/bind
}
function create_tap() {
# create a unique tap device for each VM
tap=$1
tap_exist=$(ip a | grep "$tap" | awk '{print $1}')
if [ "$tap_exist"x != "x" ]; then
echo "$tap TAP device already available, reusing it."
else
ip tuntap add dev $tap mode tap
fi
# if acrn-br0 exists, add VM's unique tap device under it
br_exist=$(ip a | grep acrn-br0 | awk '{print $1}')
if [ "$br_exist"x != "x" -a "$tap_exist"x = "x" ]; then
echo "acrn-br0 bridge already exists, adding new $tap TAP device to it..."
ip link set "$tap" master acrn-br0
ip link set dev "$tap" down
ip link set dev "$tap" up
fi
}
function mount_partition() {
partition=$1
tmpdir=`mktemp -d`
mount ${partition} ${tmpdir}
echo ${tmpdir}
}
function unmount_partition() {
tmpdir=$1
umount ${tmpdir}
rmdir ${tmpdir}
}
# Generators of device model parameters
function add_cpus() {
# Each parameter of this function is considered the processor ID (as is reported in /proc/cpuinfo) of a CPU assigned
# to a post-launched RTVM.
if [ "${rtos_type}" != "no" ]; then
offline_cpus $*
fi
cpu_list=$(local IFS=, ; echo "$*")
echo -n "--cpu_affinity ${cpu_list}"
}
function add_rtvm_options() {
if [ "${rtos_type}" = "Soft RT" ]; then
echo -n "--rtvm"
elif [ "${rtos_type}" = "Hard RT" ]; then
echo -n "--rtvm --lapic_pt"
fi
}
function add_interrupt_storm_monitor() {
threshold_per_sec=$1
probe_period_in_sec=$2
inject_delay_in_ms=$3
delay_duration_in_ms=$4
echo -n "--intr_monitor ${threshold_per_sec},${probe_period_in_sec},${inject_delay_in_ms},${delay_duration_in_ms}"
}
function add_logger_settings() {
loggers=()
for conf in $*; do
logger=${conf%=*}
level=${conf#*=}
loggers+=("${logger},level=${level}")
done
cmd_param=$(local IFS=';' ; echo "${loggers[*]}")
echo -n "--logger_setting ${cmd_param}"
}
function add_virtual_device() {
slot=$1
kind=$2
options=$3
if [ "${kind}" = "virtio-net" ]; then
# Create the tap device
tap_conf=${options%,*}
create_tap "tap_${tap_conf#tap=}" >> /dev/stderr
fi
echo -n "-s ${slot},${kind}"
if [ -n "${options}" ]; then
echo -n ",${options}"
fi
}
function add_passthrough_device() {
slot=$1
physical_bdf=$2
options=$3
unbind_device $physical_bdf
# bus, device and function as decimal integers
bus_temp=${physical_bdf#*:}; bus=$((16#${bus_temp%:*}))
dev_temp=${physical_bdf##*:}; dev=$((16#${dev_temp%.*}))
fun=$((16#${physical_bdf#*.}))
echo -n "-s "
printf '%s,passthru,%x/%x/%x' ${slot} ${bus} ${dev} ${fun}
if [ -n "${options}" ]; then
echo -n ",${options}"
fi
}