1# Copyright (C) 2019-2022 Intel Corporation.
2#
3# SPDX-License-Identifier: BSD-3-Clause
4#
5
6import os
7import sys
8import copy
9from defusedxml.lxml import parse, tostring
10sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'library'))
11sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'hv_config'))
12sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'acpi_gen'))
13from scenario_item import HwInfo, VmInfo
14import board_cfg_lib
15import scenario_cfg_lib
16import acrn_config_utilities
17import hv_cfg_lib
18import board_defconfig
19from hv_item import HvInfo
20import asl_gen
21
22try:
23    import xmlschema
24except ImportError:
25    pass
26
27ACRN_PATH = acrn_config_utilities.SOURCE_ROOT_DIR
28ACRN_CONFIG_DEF = ACRN_PATH + 'misc/config_tools/data/'
29GEN_FILE = ["vm_configurations.h", "vm_configurations.c", "pci_dev.c", ".config", "ivshmem_cfg.h", "pt_intx.c"]
30
31
32def get_scenario_item_values(board_info, scenario_info):
33    """
34    Glue code to provide user selectable options to config UI tool.
35    Return a dictionary of key-value pairs containing features and corresponding lists of
36    user selectable values to the config UI tool.
37    :param board_info: file that contains board information
38    """
39    hv_cfg_lib.ERR_LIST = {}
40    scenario_item_values = {}
41    hw_info = HwInfo(board_info)
42    hv_info = HvInfo(scenario_info)
43
44    # get vm count
45    acrn_config_utilities.BOARD_INFO_FILE = board_info
46    acrn_config_utilities.SCENARIO_INFO_FILE = scenario_info
47    acrn_config_utilities.get_vm_num(scenario_info)
48    acrn_config_utilities.get_load_order()
49
50    # per scenario
51    guest_flags = copy.deepcopy(acrn_config_utilities.GUEST_FLAG)
52    guest_flags.remove('0UL')
53    scenario_item_values['vm,vm_type'] = scenario_cfg_lib.LOAD_VM_TYPE
54    scenario_item_values["vm,cpu_affinity"] = hw_info.get_processor_val()
55    scenario_item_values["vm,guest_flags"] = guest_flags
56    scenario_item_values["vm,clos,vcpu_clos"] = hw_info.get_clos_val()
57    scenario_item_values["vm,pci_devs"] = scenario_cfg_lib.avl_pci_devs()
58    scenario_item_values["vm,os_config,kern_type"] = scenario_cfg_lib.KERN_TYPE_LIST
59    scenario_item_values["vm,mmio_resources,p2sb"] = hv_cfg_lib.N_Y
60    scenario_item_values["vm,mmio_resources,TPM2"] = hv_cfg_lib.N_Y
61    scenario_item_values.update(scenario_cfg_lib.avl_vuart_ui_select(scenario_info))
62    scenario_item_values["vm,console_vuart,base"] = ['INVALID_PCI_BASE', 'PCI_VUART']
63    scenario_item_values["vm,communication_vuart,base"] = ['INVALID_PCI_BASE', 'PCI_VUART']
64
65    # board
66
67    scenario_item_values["hv,DEBUG_OPTIONS,RELEASE"] = hv_cfg_lib.N_Y
68    scenario_item_values["hv,DEBUG_OPTIONS,NPK_LOGLEVEL"] = hv_cfg_lib.get_select_range("DEBUG_OPTIONS", "LOG_LEVEL")
69    scenario_item_values["hv,DEBUG_OPTIONS,MEM_LOGLEVEL"] = hv_cfg_lib.get_select_range("DEBUG_OPTIONS", "LOG_LEVEL")
70    scenario_item_values["hv,DEBUG_OPTIONS,CONSOLE_LOGLEVEL"] = hv_cfg_lib.get_select_range("DEBUG_OPTIONS", "LOG_LEVEL")
71    scenario_item_values["hv,DEBUG_OPTIONS,SERIAL_CONSOLE"] = board_cfg_lib.get_native_ttys_info(board_info)
72
73    scenario_item_values["hv,CAPACITIES,MAX_IOAPIC_NUM"] = hv_cfg_lib.get_select_range("CAPACITIES", "IOAPIC_NUM")
74
75    scenario_item_values["hv,FEATURES,MULTIBOOT2_ENABLED"] = hv_cfg_lib.N_Y
76    scenario_item_values["hv,FEATURES,RDT,RDT_ENABLED"] = board_cfg_lib.get_rdt_select_opt()
77    scenario_item_values["hv,FEATURES,RDT,CDP_ENABLED"] = board_cfg_lib.get_rdt_select_opt()
78    scenario_item_values["hv,FEATURES,SCHEDULER"] = hv_cfg_lib.SCHEDULER_TYPE
79    scenario_item_values["hv,FEATURES,RELOC_ENABLED"] = hv_cfg_lib.N_Y
80    scenario_item_values["hv,FEATURES,HYPERV_ENABLED"] = hv_cfg_lib.N_Y
81    scenario_item_values["hv,FEATURES,ACPI_PARSE_ENABLED"] = hv_cfg_lib.N_Y
82    scenario_item_values["hv,FEATURES,L1D_VMENTRY_ENABLED"] = hv_cfg_lib.N_Y
83    scenario_item_values["hv,FEATURES,MCE_ON_PSC_DISABLED"] = hv_cfg_lib.N_Y
84    scenario_item_values["hv,FEATURES,IOMMU_ENFORCE_SNP"] = hv_cfg_lib.N_Y
85    scenario_item_values["hv,FEATURES,IVSHMEM,IVSHMEM_ENABLED"] = hv_cfg_lib.N_Y
86    scenario_item_values["hv,FEATURES,SSRAM,SSRAM_ENABLED"] = hv_cfg_lib.N_Y
87
88    scenario_cfg_lib.ERR_LIST.update(hv_cfg_lib.ERR_LIST)
89    return scenario_item_values
90
91
92def validate_scenario_schema(scenario_info):
93    """
94    Validate settings in scenario xml if there is scenario schema
95    :param xsd_doc: scenario schema
96    :param scenario_info: scenario file
97    """
98
99    """
100    XMLSchema does not process XInclude.
101    Use lxml to expand the schema which is feed to XMLSchema as a string.
102    """
103    xsd_doc = parse(acrn_config_utilities.SCENARIO_SCHEMA_FILE)
104    xsd_doc.xinclude()
105    my_schema = xmlschema.XMLSchema11(tostring(xsd_doc, encoding="unicode"))
106
107    it = my_schema.iter_errors(scenario_info)
108    for idx, validation_error in enumerate(it, start=1):
109        key = ""
110        if not validation_error:
111            continue
112        else:
113            path = str(validation_error.path).split("/")
114            cnt = 0
115            for p in path:
116                if '[' in p:
117                    idx = int(p.split("[")[1].split("]")[0]) - 1
118                    p = p.split("[")[0] + ":id=" + str(idx)
119                    path[cnt] = p
120                cnt = cnt + 1
121            key =','.join(path[2:])
122            element = "'" + path[-1] + "' "
123            reason = validation_error.reason + ": last call: " + str(validation_error.obj)
124            scenario_cfg_lib.ERR_LIST[key] = element + reason
125
126def apply_data_checks(board_info, scenario_info):
127    xsd_doc = parse(acrn_config_utilities.DATACHECK_SCHEMA_FILE)
128    xsd_doc.xinclude()
129    datachecks_schema = xmlschema.XMLSchema11(tostring(xsd_doc, encoding="unicode"))
130
131    main_etree = parse(board_info)
132    scenario_etree = parse(scenario_info)
133    main_etree.getroot().extend(scenario_etree.getroot()[:])
134    # FIXME: Figure out proper error keys for data check failures
135    error_key = ""
136
137    it = datachecks_schema.iter_errors(main_etree)
138    for idx, error in enumerate(it, start=1):
139        anno = error.validator.annotation
140        description = anno.documentation[0].text
141        severity = anno.elem.get("{https://projectacrn.org}severity")
142
143        if severity == "error":
144            if error_key in scenario_cfg_lib.ERR_LIST.keys():
145                scenario_cfg_lib.ERR_LIST[error_key].append("\n" + description)
146            else:
147                scenario_cfg_lib.ERR_LIST[error_key] = description
148
149def validate_scenario_setting(board_info, scenario_info):
150    hv_cfg_lib.ERR_LIST = {}
151    scenario_cfg_lib.ERR_LIST = {}
152
153    if "xmlschema" in sys.modules.keys():
154        validate_scenario_schema(scenario_info)
155        apply_data_checks(board_info, scenario_info)
156
157    """
158    Validate settings in scenario xml
159    :param board_info: board file
160    :param scenario_info: scenario file
161    :return: return a dictionary that contains errors
162    """
163    acrn_config_utilities.BOARD_INFO_FILE = board_info
164    acrn_config_utilities.SCENARIO_INFO_FILE = scenario_info
165
166    hv_info = HvInfo(scenario_info)
167    hv_info.get_info()
168    hv_info.check_item()
169
170    scenario_info_items = {}
171    vm_info = VmInfo(board_info, scenario_info)
172    vm_info.get_info()
173    vm_info.set_ivshmem(hv_info.mem.ivshmem_region)
174    vm_info.check_item()
175
176    scenario_info_items['vm'] = vm_info
177    scenario_info_items['hv'] = hv_info
178
179    scenario_cfg_lib.ERR_LIST.update(hv_cfg_lib.ERR_LIST)
180    return (scenario_cfg_lib.ERR_LIST, scenario_info_items)
181
182
183def main(args):
184    """
185    Generate board related source code
186    :param args: command line args
187    """
188    err_dic = {}
189
190    (err_dic, params) = acrn_config_utilities.get_param(args)
191    if err_dic:
192        return err_dic
193
194    # check env
195    err_dic = acrn_config_utilities.prepare()
196    if err_dic:
197        return err_dic
198
199    acrn_config_utilities.BOARD_INFO_FILE = params['--board']
200    acrn_config_utilities.SCENARIO_INFO_FILE = params['--scenario']
201    acrn_config_utilities.get_vm_num(params['--scenario'])
202    acrn_config_utilities.get_load_order()
203
204    # get board name
205    (err_dic, board_name) = acrn_config_utilities.get_board_name()
206
207    # get scenario name
208    (err_dic, scenario) = acrn_config_utilities.get_scenario_name()
209    if err_dic:
210        return err_dic
211
212    if acrn_config_utilities.VM_COUNT > acrn_config_utilities.MAX_VM_NUM:
213        err_dic['vm count'] = "Number of VMs in scenario xml file should be no greater than hv/CAPACITIES/MAX_VM_NUM ! " \
214                              "Now this value is {}.".format(acrn_config_utilities.MAX_VM_NUM)
215        return err_dic
216
217    if params['--out']:
218        if os.path.isabs(params['--out']):
219            scen_output = params['--out'] + "/scenarios/" + scenario + "/"
220        else:
221            scen_output = ACRN_PATH + params['--out'] + "/scenarios/" + scenario + "/"
222    else:
223        scen_output = ACRN_CONFIG_DEF + "/" + scenario + "/"
224
225    scen_board = scen_output + "/"
226    acrn_config_utilities.mkdir(scen_board)
227    acrn_config_utilities.mkdir(scen_output)
228
229    vm_config_h  = scen_output + GEN_FILE[0]
230    vm_config_c  = scen_output + GEN_FILE[1]
231    pci_config_c = scen_board + GEN_FILE[2]
232    config_hv = scen_board + board_name + GEN_FILE[3]
233    ivshmem_config_h = scen_board + GEN_FILE[4]
234    pt_intx_config_c = scen_board + GEN_FILE[5]
235
236    # parse the scenario.xml
237    get_scenario_item_values(params['--board'], params['--scenario'])
238    (err_dic, scenario_items) = validate_scenario_setting(params['--board'], params['--scenario'])
239    if err_dic:
240        acrn_config_utilities.print_red("Scenario xml file validation failed:", err=True)
241        return err_dic
242
243    # generate board defconfig
244    with open(config_hv, 'w+') as config:
245        err_dic = board_defconfig.generate_file(scenario_items['hv'], config)
246        if err_dic:
247            return err_dic
248
249    # generate ASL code of ACPI tables for Pre-launched VMs
250    if not err_dic:
251        err_dic = asl_gen.main(args)
252
253    if not err_dic:
254        print("Scenario configuration files were created successfully.")
255    else:
256        print("Failed to create scenario configuration files.")
257
258    return err_dic
259
260
261def ui_entry_api(board_info, scenario_info, out=''):
262
263    arg_list = ['scenario_cfg_gen.py', '--board', board_info, '--scenario', scenario_info, '--out', out]
264
265    err_dic = acrn_config_utilities.prepare()
266    if err_dic:
267        return err_dic
268
269    err_dic = main(arg_list)
270
271    return err_dic
272
273
274if __name__ == '__main__':
275
276    ARGS = sys.argv
277    err_dic = main(ARGS)
278    if err_dic:
279        for err_k, err_v in err_dic.items():
280            acrn_config_utilities.print_red("{}: {}".format(err_k, err_v), err=True)
281    sys.exit(1 if err_dic else 0)
282