acrn-hypervisor/misc/acrn-config/library/launch_cfg_lib.py
Wei Liu ccef1af1f0 acrn-config: remove hard code UUID from config xmls
To keep align hv source code changes, config tool doese below changes:
1. Remove UUID from scenario config files.
2. Remove severity from scenario config files.
3. Use vm type to instead load order type.
4. Use the mapped UUID data base for launch vm script configurations.
5. Unify the ui_entry_api for '--out'

Tracked-On: #4641
Signed-off-by: Wei Liu <weix.w.liu@intel.com>
Acked-by: Victor Sun <victor.sun@intel.com>
Acked-by: Terry Zou <terry.zou@intel.com>
2020-04-17 13:45:18 +08:00

559 lines
18 KiB
Python

# Copyright (C) 2019 Intel Corporation. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
import os
import getopt
import common
import board_cfg_lib
import scenario_cfg_lib
ERR_LIST = {}
BOOT_TYPE = ['no', 'vsbl', 'ovmf']
RTOS_TYPE = ['no', 'Soft RT', 'Hard RT']
DM_VUART0 = ['Disable', 'Enable']
CPU_SHARING = ['Disabled', 'Enabled']
UOS_TYPES = ['CLEARLINUX', 'ANDROID', 'ALIOS', 'PREEMPT-RT LINUX', 'VXWORKS', 'WINDOWS', 'ZEPHYR', 'GENERIC LINUX']
PT_SUB_PCI = {}
PT_SUB_PCI['usb_xdci'] = ['USB controller']
PT_SUB_PCI['ipu'] = ['Multimedia controller']
PT_SUB_PCI['ipu_i2c'] = ['Signal processing controller']
PT_SUB_PCI['cse'] = ['Communication controller']
PT_SUB_PCI['audio'] = ['Audio device', 'Multimedia audio controller']
PT_SUB_PCI['audio_codec'] = ['Signal processing controller']
PT_SUB_PCI['sd_card'] = ['SD Host controller']
PT_SUB_PCI['wifi'] = ['Ethernet controller', 'Network controller', '802.1a controller',
'802.1b controller', 'Wireless controller']
PT_SUB_PCI['bluetooth'] = ['Signal processing controller']
PT_SUB_PCI['ethernet'] = ['Ethernet controller', 'Network controller']
PT_SUB_PCI['sata'] = ['SATA controller']
PT_SUB_PCI['nvme'] = ['Non-Volatile memory controller']
# passthrough devices for board
PASSTHRU_DEVS = ['usb_xdci', 'ipu', 'ipu_i2c', 'cse', 'audio', 'sata',
'nvme', 'audio_codec', 'sd_card', 'ethernet', 'wifi', 'bluetooth']
PT_SLOT = {
"hostbridge":0,
"lpc":1,
"pci-gvt":2,
"virtio-blk":3,
"audio_codec":24
}
POST_UUID_DIC = {}
PM_CHANNEL = ['', 'IOC', 'PowerButton', 'vuart1(pty)', 'vuart1(tty)']
PM_CHANNEL_DIC = {
None:'',
'IOC':'--pm_notify_channel ioc',
'PowerButton':'--pm_notify_channel power_button',
'vuart1(pty)':'--pm_notify_channel uart \\\n --pm_by_vuart pty,/run/acrn/life_mngr_$vm_name \\\n -l com2,/run/acrn/life_mngr_$vm_name',
'vuart1(tty)':'--pm_notify_channel uart --pm_by_vuart tty,/dev/ttyS1',
}
MOUNT_FLAG_DIC = {}
GPU_BDF = "00:02.0"
def usage(file_name):
""" This is usage for how to use this tool """
print("usage= {} [h]".format(file_name), end="")
print("--board <board_info_file> --scenario <scenario_info_file> --launch <launch_info_file> --uosid <uosid id> --out [output folder]")
print('board_info_file : file name of the board info')
print('scenario_info_file : file name of the scenario info')
print('launch_info_file : file name of the launch info')
print('uosid : this is the relateive id for post launch vm in scenario info XML:[1..max post launch vm]')
print('output folder : path to acrn-hypervisor_folder')
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
"""
vm_th = '0'
err_dic = {}
board_info_file = False
scenario_info_file = False
launch_info_file = False
output_folder = False
param_list = ['--board', '--scenario', '--launch', '--uosid']
for arg_str in param_list:
if arg_str not in args:
usage(args[0])
err_dic['common error: get wrong parameter'] = "wrong usage"
return (err_dic, board_info_file, scenario_info_file, launch_info_file, int(vm_th), output_folder)
args_list = args[1:]
(optlist, args_list) = getopt.getopt(args_list, '', ['board=', 'scenario=', 'launch=', 'uosid=', 'out='])
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 == '--launch':
launch_info_file = arg_v
if arg_k == '--out':
output_folder = arg_v
if '--uosid' in args:
if arg_k == '--uosid':
vm_th = arg_v
if not vm_th.isnumeric():
err_dic['common error: get wrong parameter'] = "--uosid should be a number"
return (err_dic, board_info_file, scenario_info_file, launch_info_file, int(vm_th), output_folder)
if not board_info_file or not scenario_info_file or not launch_info_file:
usage(args[0])
err_dic['common error: get wrong parameter'] = "wrong usage"
return (err_dic, board_info_file, scenario_info_file, launch_info_file, int(vm_th), output_folder)
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, launch_info_file, int(vm_th), output_folder)
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, launch_info_file, int(vm_th), output_folder)
if not os.path.exists(launch_info_file):
err_dic['common error: get wrong parameter'] = "{} is not exist!".format(launch_info_file)
return (err_dic, board_info_file, scenario_info_file, launch_info_file, int(vm_th), output_folder)
return (err_dic, board_info_file, scenario_info_file, launch_info_file, int(vm_th), output_folder)
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 = common.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():
"""
Get post vm number list
:return: total post dic: {launch_id:scenario_id} in launch file
"""
post_vm_list = []
# get post vm number
root = common.get_config_root(common.LAUNCH_INFO_FILE)
for item in root:
if item.tag == "uos":
post_vm_list.append(int(item.attrib['id']))
return post_vm_list
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
for vm_type in common.VM_TYPES.values():
if scenario_cfg_lib.VM_DB[vm_type]['load_type'] == "POST_LAUNCHED_VM":
post_launch_cnt += 1
return post_launch_cnt
def get_post_vm_cnt():
"""
Get board name from launch.xml at fist line
:param scenario_file: it is a file what contains scenario information for script to read from
"""
launch_vm_count = launch_vm_cnt(common.LAUNCH_INFO_FILE)
post_vm_count = post_vm_cnt(common.SCENARIO_INFO_FILE)
return (launch_vm_count, post_vm_count)
def is_config_file_match():
match = True
# check if the board config match scenario config
(err_dic, scenario_for_board) = common.get_xml_attrib(common.SCENARIO_INFO_FILE, "board")
(err_dic, board_name) = common.get_xml_attrib(common.BOARD_INFO_FILE, "board")
if scenario_for_board != board_name:
err_dic['scenario config: Not match'] = "The board xml and scenario xml should be matched!"
match = False
# check if the board config match launch config
(err_dic, launch_for_board) = common.get_xml_attrib(common.LAUNCH_INFO_FILE, "board")
if launch_for_board != board_name:
err_dic['launch config: Not match'] = "The board xml and launch xml should be matched!"
match = False
return (err_dic, match)
def get_scenario_uuid(uosid):
# {id_num:uuid} (id_num:0~max)
scenario_uuid = ''
if common.VM_TYPES[uosid] == "KATA_VM" or common.VM_TYPES[uosid] == "POST_RT_VM":
return scenario_cfg_lib.VM_DB[common.VM_TYPES[uosid]]['uuid']
i_cnt = 0
for vm_i,vm_type in common.VM_TYPES.items():
if vm_type == "POST_STD_VM" and vm_i <= uosid:
i_cnt += 1
if i_cnt > 0:
i_cnt = 0
scenario_uuid = scenario_cfg_lib.VM_DB["POST_STD_VM"]['uuid'][i_cnt]
return scenario_uuid
def get_sos_vmid():
sos_id = ''
for vm_i,vm_type in common.VM_TYPES.items():
if vm_type == "SOS_VM":
sos_id = vm_i
break
return sos_id
def get_bdf_from_tag(config_file, branch_tag, tag_str):
bdf_list = {}
bdf_list = common.get_leaf_tag_map(config_file, branch_tag, tag_str)
# split b:d:f from pci description
for idx, bdf_v in bdf_list.items():
if bdf_v:
bdf_list[idx] = bdf_v.split()[0]
return bdf_list
def get_vpid_from_bdf(bdf_vpid_map, bdf_list):
vpid_list = {}
post_vm_list = get_post_num_list()
for p_id in post_vm_list:
for bdf_k, vpid_v in bdf_vpid_map.items():
if bdf_k == bdf_list[p_id]:
# print("k:{}, v{}".format(bdf_k, bdf_list[p_id]))
# convert "808x:0xxx" to "808x 0xxx"
tmp_vpid = " ".join(vpid_v.split(':'))
vpid_list[p_id] = tmp_vpid
elif not bdf_list[p_id]:
vpid_list[p_id] = ''
return vpid_list
def get_uos_type():
"""
Get uos name from launch.xml at fist line
"""
uos_types = common.get_leaf_tag_map(common.LAUNCH_INFO_FILE, "uos_type")
return uos_types
def is_bdf_format(bdf_str):
bdf_len = 7
status = True
if not bdf_str:
return status
bdf_str_len = len(bdf_str)
if ':' in bdf_str and '.' in bdf_str and bdf_len == bdf_str_len:
status = True
else:
status = False
return status
def is_vpid_format(vpid_str):
status = True
if not vpid_str:
return status
vpid_len = 9
vpid_str_len = len(vpid_str)
if ' ' in vpid_str and vpid_len == vpid_str_len:
status = True
else:
status = False
return status
def pt_devs_check(bdf_list, vpid_list, item):
i_cnt = 1
# check bdf
for bdf_str in bdf_list.values():
if is_bdf_format(bdf_str):
continue
else:
key = "uos:id={},passthrough_devices,{}".format(i_cnt, item)
ERR_LIST[key] = "Unkonw the BDF format of {} device".format(item)
i_cnt += 1
# check vpid
i_cnt = 1
for vpid_str in vpid_list.values():
if is_vpid_format(vpid_str):
continue
else:
key = "uos:id={},passthrough_devices,{}".format(i_cnt, item)
ERR_LIST[key] = "Unkonw the Vendor:Product ID format of {} device".format(item)
i_cnt += 1
def empty_err(i_cnt, item):
"""
add empty error message into ERR_LIST
:param i_cnt: the launch vm index from config xml
:param item: the item of tag from config xml
:return: None
"""
key = "uos:id={},{}".format(i_cnt, item)
ERR_LIST[key] = "The parameter should not be empty"
def args_aval_check(arg_list, item, avl_list):
"""
check arguments from config xml are available and validate
:param arg_list: the list of arguments from config xml
:param item: the item of tag from config xml
:param avl_list: available argument which are allowed to chose
:return: None
"""
# args should be set into launch xml from webUI
i_cnt = 1
skip_check_list = ['']
if item in skip_check_list:
return
for arg_str in arg_list.values():
if arg_str == None or not arg_str.strip():
empty_err(i_cnt, item)
i_cnt += 1
continue
if arg_str not in avl_list:
key = "uos:id={},{}".format(i_cnt, item)
ERR_LIST[key] = "The {} is invalidate".format(item)
i_cnt += 1
def mem_size_check(arg_list, item):
"""
check memory size list which are set from webUI
:param arg_list: the list of arguments from config xml
:param item: the item of tag from config xml
:return: None
"""
# get total memory information
total_mem_mb = board_cfg_lib.get_total_mem()
# available check
i_cnt = 1
for arg_str in arg_list.values():
if arg_str == None or not arg_str.strip():
empty_err(i_cnt, item)
i_cnt += 1
continue
mem_size_set = int(arg_str.strip())
if mem_size_set > total_mem_mb:
key = "uos:id={},{}".format(i_cnt, item)
ERR_LIST[key] = "{}MB should be less than total memory {}MB".format(item)
i_cnt += 1
def virtual_dev_slot(dev):
max_slot = 31
base_slot = 3
# get devices slot which already stored
if dev in list(PT_SLOT.keys()):
return PT_SLOT[dev]
# alloc a new slot for device
for slot_num in range(base_slot, max_slot):
if slot_num not in list(PT_SLOT.values()):
if (slot_num == 6 and 14 in list(PT_SLOT.values())) or (slot_num == 14 and 6 in list(PT_SLOT.values())):
continue
if (slot_num == 7 and 15 in list(PT_SLOT.values())) or (slot_num == 15 and 7 in list(PT_SLOT.values())):
continue
PT_SLOT[dev] = slot_num
break
return slot_num
def get_slot(bdf_list, dev):
slot_list = {}
post_vm_list = get_post_num_list()
for p_id in post_vm_list:
if not bdf_list[p_id]:
slot_list[p_id] = ''
else:
bus = int(bdf_list[p_id][0:2], 16)
slot = int(bdf_list[p_id][3:5], 16)
fun = int(bdf_list[p_id][6:7], 16)
slot_fun = str(bus) + ":" + str(slot) + ":" + str(fun)
if bus != 0:
slot_fun = virtual_dev_slot(dev)
PT_SLOT[dev] = slot_fun
else:
# add already used slot for pass-throught devices to avoid conflict with virtio devices
PT_SLOT[dev] = slot
slot_list[p_id] = slot_fun
return slot_list
def get_pt_dev():
""" Get passthrough device list """
cap_pt = PASSTHRU_DEVS
return cap_pt
def get_vuart1_from_scenario(vmid):
"""Get the vmid's vuart1 base"""
vuart1 = board_cfg_lib.get_vuart_info_id(common.SCENARIO_INFO_FILE, 1)
return vuart1[vmid]['base']
def pt_devs_check_audio(audio_map, audio_codec_map):
"""
Check the connections about audio/audio_codec pass-through devices
If audio_codec is selected as pass-through device, the audio device
must to be chosen as pass-through device either.
:param audio_map: the dictionary contains vmid and bdf of audio device
:param audio_codec_map: the dictionary contains vmid and bdf of audio_codec device
"""
for vmid in list(audio_map.keys()):
bdf_audio = audio_map[vmid]
bdf_codec = audio_codec_map[vmid]
if not bdf_audio and bdf_codec:
key = "uos:id={},passthrough_devices,{}".format(vmid, 'audio_codec')
ERR_LIST[key] = "Audio codec device should be pass through together with Audio devcie!"
def check_block_mount(virtio_blk_dic):
(blk_dev_list, num) = board_cfg_lib.get_rootfs(common.BOARD_INFO_FILE)
for vmid in list(virtio_blk_dic.keys()):
mount_flags = []
for blk in virtio_blk_dic[vmid]:
rootfs_img = ''
if not blk:
mount_flags.append(False)
continue
if ':' in blk:
blk_dev = blk.split(':')[0]
rootfs_img = blk.split(':')[1]
else:
blk_dev = blk
if blk_dev in blk_dev_list and rootfs_img:
mount_flags.append(True)
else:
mount_flags.append(False)
MOUNT_FLAG_DIC[vmid] = mount_flags
def cpu_sharing_check(cpu_sharing, item):
"""
Check cpu sharing status with cpu affinity setting
:param cpu_share_status:
:param item:
:return: None
"""
use_cpus = []
use_same_cpu = False
vm_cpu_share = []
vm_cpu_share_consistent = True
cpu_affinity = common.get_leaf_tag_map(common.SCENARIO_INFO_FILE, "vcpu_affinity", "pcpu_id")
for vm_i in cpu_affinity.keys():
for cpu in cpu_affinity[vm_i]:
if cpu in use_cpus:
use_same_cpu = True
else:
use_cpus.append(cpu)
for vm_i in cpu_sharing.keys():
cpu_share = cpu_sharing[vm_i]
stat_len = len(vm_cpu_share)
if stat_len != 0 and cpu_share not in vm_cpu_share:
vm_cpu_share_consistent = False
else:
vm_cpu_share.append(cpu_share)
if not vm_cpu_share_consistent:
key = "uos:id={},{}".format(vm_i, item)
ERR_LIST[key] = "CPU sharing for all VMs should be consistent to 'Disabled' or 'Enabled'"
return
if cpu_sharing[vm_i] == "Disabled" and use_same_cpu:
key = "uos:id={},{}".format(vm_i, item)
ERR_LIST[key] = "The same pcpu was configurated in scenario config, and not allow to set the cpu_sharing to 'Disabled'!"
return
def bdf_duplicate_check(bdf_dic):
"""
Check if exist duplicate slot
:param bdf_dic: contains all selected pass-through devices
:return: None
"""
bdf_used = []
for dev in bdf_dic.keys():
dev_bdf_dic = bdf_dic[dev]
for vm_i in dev_bdf_dic.keys():
dev_bdf = dev_bdf_dic[vm_i]
if not dev_bdf:
continue
if dev_bdf in bdf_used:
key = "uos:id={},{},{}".format(vm_i, 'passthrough_devices', dev)
ERR_LIST[key] = "You select the same device for {} pass-through !".format(dev)
return
else:
bdf_used.append(dev_bdf)
def get_gpu_vpid():
vpid = ''
vpid_lines = board_cfg_lib.get_info(common.BOARD_INFO_FILE, "<PCI_VID_PID>", "</PCI_VID_PID>")
for vpid_line in vpid_lines:
if GPU_BDF in vpid_line:
vpid = vpid_line.split()[2]
return vpid