# Copyright (C) 2019 Intel Corporation. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # import os import sys import getopt import shutil import subprocess import re import xml.etree.ElementTree as ET SOURCE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../../') HV_LICENSE_FILE = SOURCE_PATH + 'misc/acrn-config/library/hypervisor_license' PY_CACHES = ["__pycache__", "../board_config/__pycache__", "../scenario_config/__pycache__"] GUEST_FLAG = ["0UL", "GUEST_FLAG_SECURE_WORLD_ENABLED", "GUEST_FLAG_LAPIC_PASSTHROUGH", "GUEST_FLAG_IO_COMPLETION_POLLING", "GUEST_FLAG_HIDE_MTRR", "GUEST_FLAG_RT"] # Support 512M, 1G, 2G # pre launch less then 2G, sos vm less than 24G START_HPA_SIZE_LIST = ['0x20000000', '0x40000000', '0x80000000', 'CONFIG_SOS_RAM_SIZE'] MULTI_ITEM = ["guest_flag", "pcpu_id", "input", "block", "network"] class MultiItem(): def __init__(self): self.guest_flag = [] self.pcpu_id = [] self.vir_input = [] self.vir_block = [] self.vir_console = [] self.vir_network = [] class TmpItem(): def __init__(self): self.tag = {} self.multi = MultiItem() def open_license(): """ Get the license """ with open(HV_LICENSE_FILE, 'r') as f_licence: license_s = f_licence.read().strip() return license_s def print_if_yel(msg, warn=False): """ Print the message with 'Warning' if warn is true :param msg: the stings which will be output to STDOUT :param warn: the condition if needs to be output the color of yellow with 'Warning' """ if warn: print("\033[1;33mWarning\033[0m: "+msg) else: print("\033[1;33m{0}\033[0m".format(msg)) def print_if_red(msg, err=False): """ Print the message with 'Error' if err is true :param msg: the stings which will be output to STDOUT :param err: the condition if needs to be output the color of red with 'Error' """ if err: print("\033[1;31mError\033[0m: "+msg) else: print("\033[1;31m{0}\033[0m".format(msg)) def usage(file_name): """ This is usage for how to use this tool """ print("usage= {} [h] ".format(file_name), end="") print("--board --scenario [--enable_commit]") print('board_info_file : file name of the board info') print('scenario_info_file : file name of the scenario info') print('enable_commit: enable the flag that git add/commit the generate files to the code base. without --enable_commit will not commit this source code') def get_param(args): """ Get the script parameters from command line :param args: this the command line of string for the script without script name """ err_dic = {} board_info_file = False scenario_info_file = False enable_commit = False if '--board' not in args or '--scenario' not in args: usage(args[0]) err_dic['common error: get wrong parameter'] = "wrong usage" return (err_dic, board_info_file, scenario_info_file, enable_commit) args_list = args[1:] (optlist, args_list) = getopt.getopt(args_list, '', ['board=', 'scenario=', 'enable_commit']) for arg_k, arg_v in optlist: if arg_k == '--board': board_info_file = arg_v if arg_k == '--scenario': scenario_info_file = arg_v if arg_k == '--enable_commit': enable_commit = True if not board_info_file or not scenario_info_file: usage(args[0]) err_dic['common error: get wrong parameter'] = "wrong usage" return (err_dic, board_info_file, scenario_info_file, enable_commit) if not os.path.exists(board_info_file): err_dic['common error: get wrong parameter'] = "{} is not exist!".format(board_info_file) return (err_dic, board_info_file, scenario_info_file, enable_commit) if not os.path.exists(scenario_info_file): err_dic['common error: get wrong parameter'] = "{} is not exist!".format(scenario_info_file) return (err_dic, board_info_file, scenario_info_file, enable_commit) return (err_dic, board_info_file, scenario_info_file, enable_commit) def check_env(check_git=False): """ Prepare to check the environment """ err_dic = {} bin_list = [] if check_git: bin_list.append('git') usr_dir = os.environ['HOME'] if not os.path.isfile("{}/.gitconfig".format(usr_dir)): err_dic['commn error: check env failed'] = "git not configured!" for excute in bin_list: res = subprocess.Popen("which {}".format(excute), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) line = res.stdout.readline().decode('ascii') if not line: err_dic['commn error: check env failed'] = "'{}' not found, please install it!".format(excute) if excute == "git": res = subprocess.Popen("git tag -l", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) line = res.stdout.readline().decode("ascii") if "acrn" not in line: err_dic['commn error: check env failed'] = "Run this tool in acrn-hypervisor mainline source code!" for py_cache in PY_CACHES: if os.path.exists(py_cache): shutil.rmtree(py_cache) return err_dic def check_hpa_size(hpa_size_list): """ This is identify if the host physical size list is correct format :param hpa_size_list: host physical size list :return: True if good format """ for hpa_size in hpa_size_list: hpa_sz_strip_ul = hpa_size.strip('UL') hpa_sz_strip_u = hpa_size.strip('U') if hpa_sz_strip_u not in START_HPA_SIZE_LIST and hpa_sz_strip_ul not in START_HPA_SIZE_LIST: if '0x' not in hpa_size and '0X' not in hpa_size: return False return True def get_xml_attrib(config_file, attrib): """ Get attribute from xml at fist line :param config_file: it is a file what contains board information for script to read from :param attrib: attribute of item in xml """ value = '' err_dic = {} with open(config_file, 'rt') as fp_info: while True: line = fp_info.readline() if not line: break if 'board=' in line or 'scenario=' in line: if attrib not in line: err_dic['common error: Not match'] = "The root item is not in xml file" return (err_dic, value) attrib_list = line.split() for attrib_value in attrib_list: if attrib in attrib_value: value = attrib_value.split('"')[1].strip('"') return (err_dic, value) def get_board_info(board_info, msg_s, msg_e): """ Get information which specify by argument :param board_info: it is a file what contains board information for script to read from :param msg_s: it is a pattern of key stings what start to match from board information :param msg_e: it is a pattern of key stings what end to match from board information """ info_start = False info_end = False info_lines = [] num = len(msg_s.split()) with open(board_info, 'rt') as f_board: while True: line = f_board.readline() if not line: break if " ".join(line.split()[0:num]) == msg_s: info_start = True info_end = False continue if " ".join(line.split()[0:num]) == msg_e: info_start = False info_end = True continue if info_start and not info_end: info_lines.append(line) continue if not info_start and info_end: return info_lines def find_index_guest_flag(flag): """ Find the index in GUEST_FLAG by flag :param flag: flag contained by GUEST_FLAG :return: index of GUEST_FLAG """ if not flag or flag == '0': return '0' if not flag.isnumeric(): for i in range(len(GUEST_FLAG)): if flag == GUEST_FLAG[i]: flag_str = i return flag_str def find_tmp_flag(leaf_text): """ Get flag and append the value :param leaf_text: it is a value of guest flag item :return: a list of flag or none """ tmp_flag = [] tmp_flag = find_index_guest_flag(leaf_text) #flag_index = find_index_guest_flag(leaf_text) #if flag_index == '0': # tmp_flag.append(0) #else: # tmp_flag.append(flag_index) return tmp_flag def get_config_root(config_file): """ This is get root of xml config :param config_file: it is a file what contains information for script to read from :return: top of root entry """ # create element tree object tree = ET.parse(config_file) # get root element root = tree.getroot() return root def get_vm_count(config_file): """ Get vm number :param config_file: it is a file what contains information for script to read from :return: total vm number """ vm_count = 0 root = get_config_root(config_file) for item in root: # vm number in scenario if item.tag == "vm": vm_count += 1 return vm_count def launch_vm_cnt(config_file): """ Get post vm number :param config_file: it is a file what contains information for script to read from :return: total post vm number in launch file """ post_vm_count = 0 # get post vm number root = get_config_root(config_file) for item in root: if item.tag == "uos": post_vm_count += 1 return post_vm_count def get_post_num_list(config_file): """ Get post vm number list :param config_file: it is a file what contains information for script to read from :return: total post dic: {launch_id:scenario_id} in launch file """ post_vm_list = [] # get post vm number root = get_config_root(config_file) for item in root: if item.tag == "uos": post_vm_list.append(int(item.attrib['id'])) return post_vm_list def get_tree_tag_val(config_file, tag_str): """ This is get tag value by tag_str from config file :param config_file: it is a file what contains information for script to read from :param tag_str: it is key of pattern to config file item :return: value of tag_str item """ root = get_config_root(config_file) for item in root: if item.tag == tag_str: return item.text return False # TODO: This will be abandonment in future def get_leaf_tag_val(config_file, branch_tag, tag_str=''): """ This is get tag value by tag_str from config file :param config_file: it is a file what contains information for script to read from :param branch_tag: it is key of patter to config file branch tag item :param tag_str: it is key of pattern to config file leaf tag item :return: value of tag_str item """ tmp_tag = [] root = get_config_root(config_file) for item in root: # for each 2th level item for sub in item: tmp_flag = [] tmp_cpus = [] if sub.tag == branch_tag: if not tag_str: tmp_tag.append(sub.text) continue # for each 3rd level item for leaf in sub: if leaf.tag == tag_str and tag_str not in MULTI_ITEM and sub.tag != "vuart": tmp_tag.append(leaf.text) continue # get guest flag for logical partition vm1 if leaf.tag == "guest_flag" and tag_str == "guest_flag": t_flag = find_tmp_flag(leaf.text) tmp_flag.append(t_flag) #continue # get cpu for vm if leaf.tag == "pcpu_id" and tag_str == "pcpu_id": tmp_cpus.append(leaf.text) continue # append guest flags for each vm if tmp_flag and tag_str == "guest_flag": tmp_tag.append(tmp_flag) continue # append cpus for vm if tmp_cpus and tag_str == "pcpu_id": tmp_tag.append(tmp_cpus) continue return tmp_tag def get_leaf_value(tmp, tag_str, leaf): # get guest flag for logical partition vm1 if leaf.tag == "guest_flag" and tag_str == "guest_flag": t_flag = find_tmp_flag(leaf.text) tmp.multi.guest_flag.append(t_flag) # get cpu for vm if leaf.tag == "pcpu_id" and tag_str == "pcpu_id": tmp.multi.pcpu_id.append(leaf.text) # get virtio-input for vm if leaf.tag == "input" and tag_str == "input": tmp.multi.vir_input.append(leaf.text) # get virtio-blk for vm if leaf.tag == "block" and tag_str == "block": tmp.multi.vir_block.append(leaf.text) # get virtio-net for vm if leaf.tag == "network" and tag_str == "network": tmp.multi.vir_network.append(leaf.text) def get_sub_value(tmp, tag_str, vm_id): # append guest flags for each vm if tmp.multi.guest_flag and tag_str == "guest_flag": tmp.tag[vm_id] = tmp.multi.guest_flag tmp.tag.append(tmp.multi.guest_flag) # append cpus for vm if tmp.multi.pcpu_id and tag_str == "pcpu_id": tmp.tag[vm_id] = tmp.multi.pcpu_id # append virtio input for vm if tmp.multi.vir_input and tag_str == "input": tmp.tag[vm_id] = tmp.multi.vir_input # append virtio block for vm if tmp.multi.vir_block and tag_str == "block": tmp.tag[vm_id] = tmp.multi.vir_block # append virtio network for vm if tmp.multi.vir_network and tag_str == "network": tmp.tag[vm_id] = tmp.multi.vir_network def get_leaf_tag_map(config_file, branch_tag, tag_str): """ This is get tag value by tag_str from config file :param config_file: it is a file what contains information for script to read from :param branch_tag: it is key of patter to config file branch tag item :param tag_str: it is key of pattern to config file leaf tag item :return: value of tag_str item map """ tmp = TmpItem() root = get_config_root(config_file) for item in root: vm_id = int(item.attrib['id']) # for each 2th level item for sub in item: tmp.multi = MultiItem() if sub.tag == branch_tag: if not tag_str: if sub.text == None or not sub.text: tmp.tag[vm_id] = '' else: tmp.tag[vm_id] = sub.text continue # for each 3rd level item for leaf in sub: if leaf.tag == tag_str and tag_str not in MULTI_ITEM and sub.tag != "vuart": if leaf.text == None or not leaf.text: tmp.tag[vm_id] = '' else: tmp.tag[vm_id] = leaf.text continue get_leaf_value(tmp, tag_str, leaf) get_sub_value(tmp, tag_str, vm_id) return tmp.tag def order_type_map_vmid(config_file, vm_count): """ This is mapping table for {id:order type} :param config_file: it is a file what contains information for script to read from :param vm_count: vm number :return: table of id:order type dictionary """ order_id_dic = {} load_type_list = get_leaf_tag_val(config_file, "load_order") for i in range(vm_count): order_id_dic[i] = load_type_list[i] return order_id_dic def get_load_order_by_vmid(config_file, vm_count, idx): """ Get load order by vm id :param config_file: it is a file what contains information for script to read from :param vm_count: vm number :param idx: index of vm id :return: table of id:order type dictionary """ err_dic = {} order_id_dic = order_type_map_vmid(config_file, vm_count) if idx >= vm_count or not order_id_dic: err_dic['vm number: failue'] = "Toatal vm number is less than index number" return (err_dic, order_id_dic[idx]) def add_to_patch(srcs_list, commit_name): """ Generate patch and apply to local source code :param srcs_list: it is a list what contains source files :param commit_name: distinguish the key commit message for the patch """ err_dic = {} changes = ' '.join(srcs_list) git_add = "git add {}".format(changes) ret = subprocess.call(git_add, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) if ret: err_dic['add patch: failue'] = "Add patch failue" return err_dic # commit this changes git_commit = 'git commit -sm "acrn-config: config patch for {}"'.format(commit_name) try: ret = subprocess.call(git_commit, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) if ret < 0: err_dic['commit patch: commit patch failue'] = "Commit patch failue" except (OSError, subprocess.CalledProcessError) as e: err_dic['commit patch: commit patch failue'] = "Commit patch failue" return err_dic def vm_pre_launch_cnt(config_file): """ Calculate the pre launched vm number :param config_file: it is a file what contains information for script to read from :return: number of pre launched vm """ pre_launch_cnt = 0 load_type_list = get_leaf_tag_val(config_file, "load_order") for vm_type in load_type_list: if vm_type == "PRE_LAUNCHED_VM": pre_launch_cnt += 1 return pre_launch_cnt def post_vm_cnt(config_file): """ Calculate the pre launched vm number :param config_file: it is a file what contains information for script to read from :return: number of post launched vm """ post_launch_cnt = 0 load_type_list = get_leaf_tag_val(config_file, "load_order") for vm_type in load_type_list: if vm_type == "POST_LAUNCHED_VM": post_launch_cnt += 1 return post_launch_cnt def handle_root_dev(line): """Handle if it match root device information pattern :param line: one line of information which had decoded to 'ASCII' """ for root_type in line.split(): # only support ext4 rootfs if "ext4" in root_type: return True return False def get_max_clos_mask(board_file): """ Parse CLOS information :param board_file: it is a file what contains board information for script to read from :return: type of rdt resource supported and their corresponding clos max. """ rdt_res=[] rdt_res_clos_max=[] rdt_res_mask_max=[] clos_lines = get_board_info(board_file, "", "") for line in clos_lines: if line.split(':')[0].strip() == "rdt resources supported": rdt_res = line.split(':')[1].strip() elif line.split(':')[0].strip() == "rdt resource clos max": rdt_res_clos_max = line.split(':')[1].strip() elif line.split(':')[0].strip() == "rdt resource mask max": rdt_res_mask_max = line.split(':')[1].strip() if (len(rdt_res) == 0) or (len(rdt_res_clos_max) == 0): return rdt_res, rdt_res_clos_max, rdt_res_mask_max else: return list(re.split(', |\s |,', rdt_res)), list(map(int, rdt_res_clos_max.split(','))), list(re.split(', |\s |,', rdt_res_mask_max)) def undline_name(name): """ This convert name which has contain '-' to '_' :param name: name which contain '-' and ' ' :return: name_str which contain'_' """ # convert '-' to '_' in name string name_str = "_".join(name.split('-')).upper() # stitch '_' while ' ' in name string if ' ' in name_str: name_str = "_".join(name_str.split()).upper() return name_str def get_vuart_id(tmp_vuart, leaf_tag, leaf_text): """ Get all vuart id member of class :param tmp_vuart: a dictionary to store member:value :param leaf_tag: key pattern of item tag :param leaf_text: key pattern of item tag's value :return: a dictionary to which stored member:value """ if leaf_tag == "type": tmp_vuart['type'] = leaf_text if leaf_tag == "base": tmp_vuart['base'] = leaf_text if leaf_tag == "irq": tmp_vuart['irq'] = leaf_text if leaf_tag == "target_vm_id": tmp_vuart['target_vm_id'] = leaf_text if leaf_tag == "target_uart_id": tmp_vuart['target_uart_id'] = leaf_text return tmp_vuart def get_vuart_info_id(config_file, idx): """ Get vuart information by vuart id indexx :param config_file: it is a file what contains information for script to read from :param idx: vuart index in range: [0,1] :return: dictionary which stored the vuart-id """ tmp_tag = {} vm_id = 0 root = get_config_root(config_file) for item in root: for sub in item: tmp_vuart = {} for leaf in sub: if sub.tag == "vuart" and int(sub.attrib['id']) == idx: tmp_vuart = get_vuart_id(tmp_vuart, leaf.tag, leaf.text) # append vuart for each vm if tmp_vuart and sub.tag == "vuart": tmp_tag[vm_id] = tmp_vuart if item.tag == "vm": vm_id += 1 return tmp_tag