acrn-hypervisor/misc/config_tools/acpi_gen/asl_gen.py
dongshen a3d06929e7 config-tools: retrieve physical APIC IDs and use them to fill in the ACPI MADT table
Retrieve physical APIC IDs from board xml file and use them to fill in the ACPI MADT table
for pre-Launched VMs.

Note that the config-tool will throw an error if the processors/die/core/thread tags are absent.
User needs to run board_inspector.py to regenerate the board xml file when this commit is merged,
if the processors/die/core/thread tags are missing in the board xml file.

Tracked-On: #6020
Signed-off-by: dongshen <dongsheng.x.zhang@intel.com>
2021-05-26 11:23:06 +08:00

500 lines
23 KiB
Python

# Copyright (C) 2019 Intel Corporation.
# SPDX-License-Identifier: BSD-3-Clause
"""the tool to generate ASL code of ACPI tables for Pre-launched VMs.
"""
import os, re, argparse, shutil
import xml.etree.ElementTree as ElementTree
from acpi_const import *
import board_cfg_lib
import collections
import lxml.etree
def calculate_checksum8():
'''
this function is implemented in iasl.
:return:
'''
pass
def gen_rsdp(dest_vm_acpi_path):
'''
generate rsdp.asl
:param dest_vm_acpi_path: the path to store generated ACPI asl code
:return:
'''
rsdp_asl = 'rsdp.asl'
p_xsdt_addr = r'XSDT Address : ([0-9a-fA-F]{16})'
with open(os.path.join(dest_vm_acpi_path, rsdp_asl), 'w') as dest:
lines = []
with open(os.path.join(TEMPLATE_ACPI_PATH, rsdp_asl), 'r') as src:
for line in src.readlines():
if re.search(p_xsdt_addr, line):
lines.append(re.sub(p_xsdt_addr, 'XSDT Address : {0:016X}'.format(ACPI_XSDT_ADDR), line))
else:
lines.append(line)
dest.writelines(lines)
def gen_xsdt(dest_vm_acpi_path, passthru_devices):
'''
generate xsdt.asl
:param dest_vm_acpi_path: the path to store generated ACPI asl code
:param passthru_devices: dict to store passthru device list
:return:
'''
xsdt_asl = 'xsdt.asl'
p_fadt_addr = r'ACPI Table Address 0 : ([0-9a-fA-F]{16})'
p_mcfg_addr = r'ACPI Table Address 1 : ([0-9a-fA-F]{16})'
p_madt_addr = r'ACPI Table Address 2 : ([0-9a-fA-F]{16})'
p_tpm2_addr = r'ACPI Table Address 3 : ([0-9a-fA-F]{16})'
p_ptct_addr = r'ACPI Table Address 4 : ([0-9a-fA-F]{16})'
with open(os.path.join(dest_vm_acpi_path, xsdt_asl), 'w') as dest:
lines = []
with open(os.path.join(TEMPLATE_ACPI_PATH, xsdt_asl), 'r') as src:
for line in src.readlines():
if re.search(p_fadt_addr, line):
lines.append(re.sub(p_fadt_addr, 'ACPI Table Address 0 : {0:016X}'.format(ACPI_FADT_ADDR), line))
elif re.search(p_mcfg_addr, line):
lines.append(re.sub(p_mcfg_addr, 'ACPI Table Address 1 : {0:016X}'.format(ACPI_MCFG_ADDR), line))
elif re.search(p_madt_addr, line):
lines.append(re.sub(p_madt_addr, 'ACPI Table Address 2 : {0:016X}'.format(ACPI_MADT_ADDR), line))
elif re.search(p_tpm2_addr, line):
if 'TPM2' in passthru_devices:
lines.append(re.sub(p_tpm2_addr, 'ACPI Table Address 3 : {0:016X}'.format(ACPI_TPM2_ADDR), line))
elif re.search(p_ptct_addr, line):
if 'PTCT' in passthru_devices:
lines.append(re.sub(p_ptct_addr, 'ACPI Table Address 4 : {0:016X}'.format(ACPI_PTCT_ADDR), line))
else:
lines.append(line)
dest.writelines(lines)
def gen_fadt(dest_vm_acpi_path, board_root):
'''
generate facp.asl
:param dest_vm_acpi_path: the path to store generated ACPI asl code
:param board_root: the root element of board xml
:return:
'''
fadt_asl = 'facp.asl'
p_facs_addr = r'FACS Address : ([0-9a-fA-F]{8})'
p_dsdt_addr = r'DSDT Address : ([0-9a-fA-F]{8})$'
with open(os.path.join(dest_vm_acpi_path, fadt_asl), 'w') as dest:
lines = []
with open(os.path.join(TEMPLATE_ACPI_PATH, fadt_asl), 'r') as src:
for line in src.readlines():
if re.search(p_facs_addr, line):
lines.append(re.sub(p_facs_addr, 'FACS Address : {0:08X}'.format(ACPI_FACS_ADDR), line))
elif re.search(p_dsdt_addr, line):
lines.append(re.sub(p_dsdt_addr, 'DSDT Address : {0:08X}'.format(ACPI_DSDT_ADDR), line))
else:
lines.append(line)
dest.writelines(lines)
def gen_mcfg(dest_vm_acpi_path):
'''
generate mcfg.asl
:param dest_vm_acpi_path: the path to store generated ACPI asl code
:return:
'''
mcfg_asl = 'mcfg.asl'
p_base_addr = r'Base Address : ([0-9a-fA-F]{16})'
p_segment_group_num = r'Segment Group Number : (\d+)'
p_start_bus_num = r'Start Bus Number : (\d+)'
p_end_bus_num = r'End Bus Number : ([0-9a-fA-F]{2})'
with open(os.path.join(dest_vm_acpi_path, mcfg_asl), 'w') as dest:
lines = []
with open(os.path.join(TEMPLATE_ACPI_PATH, mcfg_asl), 'r') as src:
for line in src.readlines():
if re.search(p_base_addr, line):
lines.append(re.sub(p_base_addr, 'Base Address : {0:016X}'.format(VIRT_PCI_MMCFG_BASE), line))
elif re.search(p_segment_group_num, line):
lines.append(re.sub(p_segment_group_num, 'Segment Group Number : {0:04X}'.format(0), line))
elif re.search(p_start_bus_num, line):
lines.append(re.sub(p_start_bus_num, 'Start Bus Number : {0:02X}'.format(0), line))
elif re.search(p_end_bus_num, line):
lines.append(re.sub(p_end_bus_num, 'End Bus Number : {0:02X}'.format(0xff), line))
else:
lines.append(line)
dest.writelines(lines)
def gen_madt(dest_vm_acpi_path, max_cpu_num, apic_ids):
'''
generate apic.asl
:param dest_vm_acpi_path: the path to store generated ACPI asl code
:return:
'''
madt_asl = 'apic.asl'
lapic_index = 0
p_lapic_addr = r'Local Apic Address : ([0-9a-fA-F]{8})'
p_flags = r'\[0004\] Flags (decoded below) : (\d{8})' # dup flags
flags_index = 0
p_lapic_index = 0
p_lapic_type = r'Subtable Type : (\d+) \[Processor Local APIC\]'
p_lapic_len = r'\[0001\] Length : ([0-9a-fA-F]{2})' # dup len
p_lapic_len_index = 0
p_lapic_flags_index = 0
p_lapic_process_id = r'\[0001\] Processor ID : (\d+)' # dup processor
p_lapic_process_id_index = 0
p_lapic_id = r'Local Apic ID : ([0-9a-fA-F]{2})'
p_lapic_line_index = 0
lapic_lines = []
ioapic_index = 0
p_ioapic_type = r'Subtable Type : (\d+) \[I/O APIC\]'
p_ioapic_len_index = 0
p_ioapic_id = r'I/O Apic ID : (\d+)'
p_ioapic_addr = r'\[0004\] Address : ([0-9a-fA-F]{8})'
lapic_nmi_index = 0
p_lapic_nmi_type = r'Subtable Type : (\d+) \[Local APIC NMI\]'
p_lapic_nmi_len_index = 0
p_lapic_nmi_processor_id_index = 0
p_lapic_nmi_flags = r'\[0002\] Flags (decoded below) : ([0-9a-fA-F]{4})'
p_lapic_nmi_flags_index = 0
p_lapic_nmi_lint = r'Interrupt Input LINT : (\d+)'
with open(os.path.join(dest_vm_acpi_path, madt_asl), 'w') as dest:
lines = []
with open(os.path.join(TEMPLATE_ACPI_PATH, madt_asl), 'r') as src:
for line in src.readlines():
if re.search(p_lapic_addr, line):
lapic_index += 1
lines.append(re.sub(p_lapic_addr, 'Local Apic Address : {0:08X}'.format(0xFEE00000), line))
elif re.search(p_flags, line):
if lapic_index == 1 and flags_index == 0:
lines.append(
re.sub(p_flags, '[0004] Flags (decoded below) : {0:08X}'.format(0x1), line))
flags_index += 1
elif p_lapic_index == 1 and p_lapic_flags_index == 0:
lines.append(
re.sub(p_flags, '[0004] Flags (decoded below) : {0:08X}'.format(0x1),
line))
p_lapic_flags_index += 1
else:
lines.append(line)
elif re.search(p_lapic_type, line):
p_lapic_index += 1
if lapic_index == 1:
lines.append(re.sub(p_lapic_type, 'Subtable Type : {0:02X} [Processor Local APIC]'.format(
ACPI_MADT_TYPE_LOCAL_APIC), line))
else:
lines.append(line)
elif re.search(p_lapic_len, line):
if p_lapic_index == 1 and p_lapic_len_index == 0:
lines.append(
re.sub(p_lapic_len, '[0001] Length : {0:02X}'.format(0x8),
line))
p_lapic_len_index += 1
elif ioapic_index == 1 and p_ioapic_len_index == 0:
lines.append(
re.sub(p_lapic_len, '[0001] Length : {0:02X}'.format(0x0C),
line))
p_ioapic_len_index += 1
elif lapic_nmi_index == 1 and p_lapic_nmi_len_index == 0:
lines.append(
re.sub(p_lapic_len, '[0001] Length : {0:02X}'.format(0x06),
line))
p_lapic_nmi_len_index += 1
else:
lines.append(line)
elif re.search(p_lapic_process_id, line):
if p_lapic_index == 1 and p_lapic_process_id_index == 0:
lines.append(re.sub(p_lapic_process_id,
'[0001] Processor ID : {0:02X}'.format(0x0),
line))
p_lapic_process_id_index += 1
elif lapic_nmi_index == 1 and p_lapic_nmi_processor_id_index == 0:
lines.append(
re.sub(p_lapic_process_id,
'[0001] Processor ID : {0:02X}'.format(0xFF),
line))
p_lapic_nmi_processor_id_index += 1
else:
lines.append(line)
elif re.search(p_lapic_id, line):
lines.append(re.sub(p_lapic_id, 'Local Apic ID : {0:02X}'.format(apic_ids[0]), line))
elif re.search(p_ioapic_type, line):
ioapic_index += 1
lines.append(
re.sub(p_ioapic_type, 'Subtable Type : {0:02X} [I/O APIC]'.format(ACPI_MADT_TYPE_IOAPIC), line))
elif re.search(p_ioapic_id, line):
lines.append(re.sub(p_ioapic_id, 'I/O Apic ID : {0:02X}'.format(0x01), line))
elif re.search(p_ioapic_addr, line):
lines.append(re.sub(p_ioapic_addr,
'[0004] Address : {0:02X}'.format(VIOAPIC_BASE),
line))
elif re.search(p_lapic_nmi_type, line):
lapic_nmi_index += 1
if lapic_nmi_index == 1:
lines.append(re.sub(p_lapic_nmi_type, 'Subtable Type : {0:02X} [Local APIC NMI]'.format(
ACPI_MADT_TYPE_LOCAL_APIC_NMI), line))
else:
lines.append(line)
elif re.search(p_lapic_nmi_flags, line):
if lapic_nmi_index == 1 and p_lapic_nmi_flags_index == 0:
lines.append(
re.sub(p_lapic_nmi_flags, '[0002] Flags (decoded below) : {0:04X}'.format(0x5),
line))
p_lapic_nmi_flags_index += 1
else:
lines.append(line)
elif re.search(p_lapic_nmi_lint, line):
if lapic_nmi_index == 1:
lines.append(re.sub(p_lapic_nmi_lint, 'Interrupt Input LINT : {0:02X}'.format(0x1), line))
else:
lines.append(line)
else:
lines.append(line)
if p_lapic_index == 1 and p_lapic_line_index < 7:
lapic_lines.append(line)
p_lapic_line_index += 1
if p_lapic_index == 1 and p_lapic_line_index == 7:
p_lapic_line_index = 0
for process_id in range(1, max_cpu_num):
p_lapic_index = process_id + 1
lines.append('\n')
for lapic_line in lapic_lines:
if re.search(p_lapic_type, lapic_line):
lines.append(re.sub(p_lapic_type,
'Subtable Type : {0:02X} [Processor Local APIC]'.format(
ACPI_MADT_TYPE_LOCAL_APIC), lapic_line))
elif re.search(p_lapic_len, lapic_line):
lines.append(
re.sub(p_lapic_len,
'[0001] Length : {0:02X}'.format(0x8),
lapic_line))
elif re.search(p_flags, lapic_line):
lines.append(
re.sub(p_flags,
'[0004] Flags (decoded below) : {0:08X}'.format(0x1),
lapic_line))
elif re.search(p_lapic_process_id, lapic_line):
lines.append(re.sub(p_lapic_process_id,
'[0001] Processor ID : {0:02X}'.format(
process_id), lapic_line))
elif re.search(p_lapic_id, lapic_line):
lines.append(
re.sub(p_lapic_id, 'Local Apic ID : {0:02X}'.format(apic_ids[process_id]), lapic_line))
else:
lines.append(lapic_line)
dest.writelines(lines)
def gen_tpm2(dest_vm_acpi_path, passthru_devices):
'''
generate tpm2.asl
:param dest_vm_acpi_path: the path to store generated ACPI asl code
:param passthru_devices: dict to store passthru device list
:return:
'''
tpm2_asl = 'tpm2.asl'
p_control_addr = r'Control Address : ([0-9a-fA-F]{16})'
p_start_method = r'Start Method : (.*)'
if 'TPM2' not in passthru_devices:
if os.path.isfile(os.path.join(dest_vm_acpi_path, tpm2_asl)):
os.remove(os.path.join(dest_vm_acpi_path, tpm2_asl))
return
with open(os.path.join(dest_vm_acpi_path, tpm2_asl), 'w') as dest:
lines = []
with open(os.path.join(TEMPLATE_ACPI_PATH, tpm2_asl), 'r') as src:
for line in src.readlines():
if re.search(p_control_addr, line):
lines.append(re.sub(p_control_addr, 'Control Address : {0:016X}'.format(0xFED40040), line))
elif re.search(p_start_method, line):
lines.append(re.sub(p_start_method, 'Start Method : {0:02X}'.format(0x7), line))
else:
lines.append(line)
dest.writelines(lines)
def gen_dsdt(dest_vm_acpi_path, passthru_devices, board_info):
'''
generate dsdt.asl
:param dest_vm_acpi_path: the path to store generated ACPI asl code
:param passthru_devices:
:return:
'''
dsdt_asl = 'dsdt.asl'
p_dsdt_start = r'{'
(bdf_desc_map, bdf_vpid_map) = board_cfg_lib.get_pci_info(board_info)
with open(os.path.join(dest_vm_acpi_path, dsdt_asl), 'w') as dest:
lines = []
with open(os.path.join(TEMPLATE_ACPI_PATH, dsdt_asl), 'r') as src:
for line in src.readlines():
lines.append(line)
if line.startswith(p_dsdt_start):
for passthru_device in passthru_devices:
passthru_bdf = None
passthru_vpid = None
for bdf in bdf_desc_map.keys():
if bdf_desc_map[bdf].find(passthru_device) >= 0:
passthru_bdf = bdf
break
if passthru_bdf is not None and passthru_bdf in bdf_vpid_map.keys():
passthru_vpid = bdf_vpid_map[passthru_bdf]
if passthru_device in ['TPM2']:
tpm2_asl = os.path.join(TEMPLATE_ACPI_PATH, 'dsdt_tpm2.asl')
start = False
with open(tpm2_asl, 'r') as tpm2_src:
for tpm2_line in tpm2_src.readlines():
if tpm2_line.startswith('{'):
start = True
continue
if tpm2_line.startswith('}'):
start = False
continue
if start:
lines.append(tpm2_line)
elif passthru_vpid is not None and passthru_vpid == TSN_DEVICE_LIST[1]:
otn1_asl = os.path.join(TEMPLATE_ACPI_PATH, 'dsdt_tsn_otn1.asl')
start = False
with open(otn1_asl, 'r') as otn1_src:
for otn1_line in otn1_src.readlines():
if otn1_line.startswith('{'):
start = True
continue
if otn1_line.startswith('}'):
start = False
continue
if start:
lines.append(otn1_line)
else:
pass
dest.writelines(lines)
def main(args):
err_dic = {}
(err_dic, params) = common.get_param(args)
if err_dic:
return err_dic
board = params['--board']
scenario= params['--scenario']
out = params['--out']
board_etree = lxml.etree.parse(board)
board_root = ElementTree.parse(board).getroot()
scenario_root = ElementTree.parse(scenario).getroot()
board_type = board_root.attrib['board']
scenario_name = scenario_root.attrib['scenario']
pcpu_list = board_root.find('CPU_PROCESSOR_INFO').text.strip().split(',')
if isinstance(pcpu_list, list):
pcpu_list = [x.strip() for x in pcpu_list]
if out is None or out == '':
DEST_ACPI_PATH = os.path.join(VM_CONFIGS_PATH, 'scenarios', scenario_name)
else:
DEST_ACPI_PATH = os.path.join(common.SOURCE_ROOT_DIR, out, 'scenarios', scenario_name)
if os.path.isdir(DEST_ACPI_PATH):
for config in os.listdir(DEST_ACPI_PATH):
if config.startswith('ACPI_VM') and os.path.isdir(os.path.join(DEST_ACPI_PATH, config)):
shutil.rmtree(os.path.join(DEST_ACPI_PATH, config))
dict_passthru_devices = collections.OrderedDict()
dict_pcpu_list = collections.OrderedDict()
for vm in scenario_root.findall('vm'):
vm_id = vm.attrib['id']
vm_type_node = vm.find('vm_type')
if (vm_type_node is not None) and (vm_type_node.text in ['PRE_STD_VM', 'SAFETY_VM', 'PRE_RT_VM']):
dict_passthru_devices[vm_id] = []
for pci_dev_node in vm.findall('pci_devs/pci_dev'):
if pci_dev_node is not None and pci_dev_node.text is not None and pci_dev_node.text.strip():
dict_passthru_devices[vm_id].append(pci_dev_node.text)
mmio_dev_nodes = vm.find('mmio_resources')
if mmio_dev_nodes is not None:
for mmio_dev_node in list(mmio_dev_nodes):
if mmio_dev_node is not None and mmio_dev_node.text.strip() == 'y':
dict_passthru_devices[vm_id].append(mmio_dev_node.tag)
dict_pcpu_list[vm_id] = []
for pcpu_id in vm.findall('cpu_affinity/pcpu_id'):
if pcpu_id is not None and pcpu_id.text.strip() in pcpu_list:
dict_pcpu_list[vm_id].append(int(pcpu_id.text))
PASSTHROUGH_PTCT = False
PRELAUNCHED_RTVM_ID = None
try:
if scenario_root.find('hv/FEATURES/SSRAM/SSRAM_ENABLED').text.strip() == 'y':
PASSTHROUGH_PTCT = True
for vm in scenario_root.findall('vm'):
vm_id = vm.attrib['id']
vm_type_node = vm.find('vm_type')
if (vm_type_node is not None) and (vm_type_node.text in ['PRE_RT_VM']):
PRELAUNCHED_RTVM_ID = vm_id
break
except:
PASSTHROUGH_PTCT = False
kern_args = common.get_leaf_tag_map(scenario, "os_config", "bootargs")
kern_type = common.get_leaf_tag_map(scenario, "os_config", "kern_type")
for vm_id, passthru_devices in dict_passthru_devices.items():
if kern_args[int(vm_id)].find('reboot=acpi') == -1 and kern_type[int(vm_id)] in ['KERNEL_BZIMAGE']:
emsg = "you need to specify 'reboot=acpi' in scenario file's bootargs for VM{}".format(vm_id)
print(emsg)
err_dic['vm,bootargs'] = emsg
break
print('start to generate ACPI ASL code for VM{}'.format(vm_id))
dest_vm_acpi_path = os.path.join(DEST_ACPI_PATH, 'ACPI_VM'+vm_id)
if not os.path.isdir(dest_vm_acpi_path):
os.makedirs(dest_vm_acpi_path)
if PASSTHROUGH_PTCT is True and vm_id == PRELAUNCHED_RTVM_ID:
passthru_devices.append(PTCT)
shutil.copy(os.path.join(VM_CONFIGS_PATH, 'acpi_template', board_type, PTCT), dest_vm_acpi_path)
gen_rsdp(dest_vm_acpi_path)
gen_xsdt(dest_vm_acpi_path, passthru_devices)
gen_fadt(dest_vm_acpi_path, board_root)
gen_mcfg(dest_vm_acpi_path)
if vm_id in dict_pcpu_list:
dict_pcpu_list[vm_id].sort()
apic_ids = []
for id in dict_pcpu_list[vm_id]:
apic_id = common.get_node(f"//processors/die/core/thread[cpu_id='{id}']/apic_id/text()", board_etree)
if apic_id is None:
emsg = 'some or all of the processors/die/core/thread/cpu_id tags are missing in board xml file for cpu {}, please run board_inspector.py to regenerate the board xml file!'.format(id)
print(emsg)
err_dic['board config: processors'] = emsg
return err_dic
else:
apic_ids.append(int(apic_id, 16))
gen_madt(dest_vm_acpi_path, len(dict_pcpu_list[vm_id]), apic_ids)
gen_tpm2(dest_vm_acpi_path, passthru_devices)
gen_dsdt(dest_vm_acpi_path, passthru_devices, board)
print('generate ASL code of ACPI tables for VM {} into {}'.format(vm_id, dest_vm_acpi_path))
else:
emsg = 'no cpu affinity config for VM {}'.format(vm_id)
print(emsg)
err_dic['vm,cpu_affinity,pcpu_id'] = emsg
return err_dic
if __name__ == '__main__':
main(sys.argv)