mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-20 04:33:55 +00:00
According to TCG ACPI specification (version 1.2), the current revision of TPM2 table, which has the optional log area fields, is 4. This patch updates the revision of vTPM2 accordingly. Tracked-On: #6288 Signed-off-by: Junjie Mao <junjie.mao@intel.com>
265 lines
12 KiB
Python
265 lines
12 KiB
Python
# Copyright (C) 2019 Intel Corporation.
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
"""the tool to generate ACPI binary for Pre-launched VMs.
|
|
|
|
"""
|
|
|
|
import logging
|
|
import subprocess # nosec
|
|
import os, sys, argparse, re, shutil
|
|
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'board_inspector'))
|
|
import lxml.etree
|
|
from acpi_const import *
|
|
import acpiparser.tpm2
|
|
import lib.cdata
|
|
import common
|
|
|
|
def asl_to_aml(dest_vm_acpi_path, dest_vm_acpi_bin_path):
|
|
'''
|
|
compile asl code of ACPI table to aml code.
|
|
:param dest_vm_acpi_path: the path of the asl code of ACPI tables
|
|
:param dest_vm_acpi_bin_path: the path of the aml code of ACPI tables
|
|
:param passthru_devices: passthrough devce list
|
|
:return:
|
|
'''
|
|
curr_path = os.getcwd()
|
|
rmsg = ''
|
|
|
|
os.chdir(dest_vm_acpi_path)
|
|
for acpi_table in ACPI_TABLE_LIST:
|
|
if acpi_table[0] == 'tpm2.asl':
|
|
if 'tpm2.asl' in os.listdir(dest_vm_acpi_path):
|
|
rc = exec_command('iasl {}'.format(acpi_table[0]))
|
|
if rc == 0 and os.path.isfile(os.path.join(dest_vm_acpi_path, acpi_table[1])):
|
|
shutil.move(os.path.join(dest_vm_acpi_path, acpi_table[1]),
|
|
os.path.join(dest_vm_acpi_bin_path, acpi_table[1]))
|
|
else:
|
|
if os.path.isfile(os.path.join(dest_vm_acpi_path, acpi_table[1])):
|
|
os.remove(os.path.join(dest_vm_acpi_path, acpi_table[1]))
|
|
rmsg = 'failed to compile {}'.format(acpi_table[0])
|
|
break
|
|
elif acpi_table[0] == 'PTCT':
|
|
if 'PTCT' in os.listdir(dest_vm_acpi_path):
|
|
shutil.copyfile(os.path.join(dest_vm_acpi_path, acpi_table[0]),
|
|
os.path.join(dest_vm_acpi_bin_path, acpi_table[1]))
|
|
elif acpi_table[0] == 'RTCT':
|
|
if 'RTCT' in os.listdir(dest_vm_acpi_path):
|
|
shutil.copyfile(os.path.join(dest_vm_acpi_path, acpi_table[0]),
|
|
os.path.join(dest_vm_acpi_bin_path, acpi_table[1]))
|
|
else:
|
|
if acpi_table[0].endswith(".asl"):
|
|
rc = exec_command('iasl {}'.format(acpi_table[0]))
|
|
if rc == 0 and os.path.isfile(os.path.join(dest_vm_acpi_path, acpi_table[1])):
|
|
shutil.move(os.path.join(dest_vm_acpi_path, acpi_table[1]),
|
|
os.path.join(dest_vm_acpi_bin_path, acpi_table[1]))
|
|
else:
|
|
if os.path.isfile(os.path.join(dest_vm_acpi_path, acpi_table[1])):
|
|
os.remove(os.path.join(dest_vm_acpi_path, acpi_table[1]))
|
|
rmsg = 'failed to compile {}'.format(acpi_table[0])
|
|
break
|
|
elif acpi_table[0].endswith(".aml"):
|
|
shutil.copy(os.path.join(dest_vm_acpi_path, acpi_table[0]),
|
|
os.path.join(dest_vm_acpi_bin_path, acpi_table[1]))
|
|
|
|
os.chdir(curr_path)
|
|
if not rmsg:
|
|
print('compile ACPI ASL code to {} successfully'.format(dest_vm_acpi_bin_path))
|
|
return rmsg
|
|
|
|
def tpm2_acpi_gen(acpi_bin, board_etree, scenario_etree, allocation_etree):
|
|
tpm2_enabled = common.get_node("//vm[@id = '0']/mmio_resources/TPM2/text()", scenario_etree)
|
|
if tpm2_enabled is not None and tpm2_enabled == 'y':
|
|
tpm2_node = common.get_node("//device[@id = 'MSFT0101' or compatible_id = 'MSFT0101']", board_etree)
|
|
if tpm2_node is not None:
|
|
_data_len = 0x4c if common.get_node("//capability[@id = 'log_area']", board_etree) is not None else 0x40
|
|
_data = bytearray(_data_len)
|
|
ctype_data = acpiparser.tpm2.TPM2(_data)
|
|
ctype_data.header.signature = "TPM2".encode()
|
|
ctype_data.header.length = _data_len
|
|
ctype_data.header.revision = 4
|
|
ctype_data.header.oemid = "ACRN ".encode()
|
|
ctype_data.header.oemtableid = "ACRNTPM2".encode()
|
|
ctype_data.header.oemrevision = 0x1
|
|
ctype_data.header.creatorid = "INTL".encode()
|
|
ctype_data.header.creatorrevision = 0x20190703
|
|
ctype_data.address_of_control_area = 0xFED40040
|
|
ctype_data.start_method = int(common.get_node("//capability[@id = 'start_method']/value/text()", tpm2_node), 16)
|
|
start_method_parameters = tpm2_node.xpath("//parameter/text()")
|
|
for i in range(len(start_method_parameters)):
|
|
ctype_data.start_method_specific_parameters[i] = int(start_method_parameters[i], 16)
|
|
if common.get_node("//capability[@id = 'log_area']", board_etree) is not None:
|
|
ctype_data.log_area_minimum_length = int(common.get_node("//log_area_minimum_length/text()", allocation_etree), 16)
|
|
ctype_data.log_area_start_address = int(common.get_node("//log_area_start_address/text()", allocation_etree), 16)
|
|
ctype_data.header.checksum = (~(sum(lib.cdata.to_bytes(ctype_data))) + 1) & 0xFF
|
|
acpi_bin.seek(ACPI_TPM2_ADDR_OFFSET)
|
|
acpi_bin.write(lib.cdata.to_bytes(ctype_data))
|
|
else:
|
|
logging.warning("Passtrhough tpm2 is enabled in scenario but the device is not presented on board.")
|
|
logging.warning("Check there is tpm2 device on board and re-generate the xml using board inspector with --advanced option.")
|
|
|
|
def aml_to_bin(dest_vm_acpi_path, dest_vm_acpi_bin_path, acpi_bin_name, board_etree, scenario_etree, allocation_etree):
|
|
'''
|
|
create the binary of ACPI table.
|
|
:param dest_vm_acpi_bin_path: the path of the aml code of ACPI tables
|
|
:param acpi_bin: the binary file name of ACPI tables
|
|
:param passthru_devices: passthrough devce list
|
|
:return:
|
|
'''
|
|
acpi_bin_file = os.path.join(dest_vm_acpi_bin_path, acpi_bin_name)
|
|
if os.path.isfile(acpi_bin_file):
|
|
os.remove(acpi_bin_file)
|
|
with open(acpi_bin_file, 'wb') as acpi_bin:
|
|
# acpi_bin.seek(ACPI_RSDP_ADDR_OFFSET)
|
|
# with open(os.path.join(dest_vm_acpi_bin_path, ACPI_TABLE_LIST[0][1]), 'rb') as asl:
|
|
# acpi_bin.write(asl.read())
|
|
|
|
acpi_bin.seek(ACPI_XSDT_ADDR_OFFSET)
|
|
with open(os.path.join(dest_vm_acpi_bin_path, ACPI_TABLE_LIST[1][1]), 'rb') as asl:
|
|
acpi_bin.write(asl.read())
|
|
|
|
acpi_bin.seek(ACPI_FADT_ADDR_OFFSET)
|
|
with open(os.path.join(dest_vm_acpi_bin_path, ACPI_TABLE_LIST[2][1]), 'rb') as asl:
|
|
acpi_bin.write(asl.read())
|
|
|
|
acpi_bin.seek(ACPI_MCFG_ADDR_OFFSET)
|
|
with open(os.path.join(dest_vm_acpi_bin_path, ACPI_TABLE_LIST[3][1]), 'rb') as asl:
|
|
acpi_bin.write(asl.read())
|
|
|
|
acpi_bin.seek(ACPI_MADT_ADDR_OFFSET)
|
|
with open(os.path.join(dest_vm_acpi_bin_path, ACPI_TABLE_LIST[4][1]), 'rb') as asl:
|
|
acpi_bin.write(asl.read())
|
|
|
|
acpi_bin.seek(ACPI_DSDT_ADDR_OFFSET)
|
|
with open(os.path.join(dest_vm_acpi_bin_path, ACPI_TABLE_LIST[6][1]), 'rb') as asl:
|
|
acpi_bin.write(asl.read())
|
|
|
|
if 'PTCT' in os.listdir(dest_vm_acpi_path):
|
|
acpi_bin.seek(ACPI_RTCT_ADDR_OFFSET)
|
|
with open(os.path.join(dest_vm_acpi_bin_path, ACPI_TABLE_LIST[7][1]), 'rb') as asl:
|
|
acpi_bin.write(asl.read())
|
|
elif 'RTCT' in os.listdir(dest_vm_acpi_path):
|
|
acpi_bin.seek(ACPI_RTCT_ADDR_OFFSET)
|
|
with open(os.path.join(dest_vm_acpi_bin_path, ACPI_TABLE_LIST[8][1]), 'rb') as asl:
|
|
acpi_bin.write(asl.read())
|
|
|
|
vm_id = acpi_bin_name.split('.')[0].split('ACPI_VM')[1]
|
|
if vm_id == '0':
|
|
tpm2_acpi_gen(acpi_bin, board_etree, scenario_etree, allocation_etree)
|
|
|
|
acpi_bin.seek(0xfffff)
|
|
acpi_bin.write(b'\0')
|
|
shutil.move(acpi_bin_file, os.path.join(dest_vm_acpi_bin_path, '..', acpi_bin_name))
|
|
print('write ACPI binary to {} successfully'.format(os.path.join(dest_vm_acpi_bin_path, '..', acpi_bin_name)))
|
|
|
|
|
|
def exec_command(cmd):
|
|
'''
|
|
execute the command and output logs.
|
|
:param cmd: the command to execute.
|
|
:return:
|
|
'''
|
|
print('exec: ', cmd)
|
|
p_compile_result = r'Compilation successful. (\d+) Errors, (\d+) Warnings, (\d+) Remarks'
|
|
cmd_list = cmd.split()
|
|
rc = 1
|
|
r_lines = []
|
|
try:
|
|
for line in subprocess.check_output(cmd_list).decode('utf8').split('\n'):
|
|
r_lines.append(line)
|
|
m = re.match(p_compile_result, line)
|
|
if m and len(m.groups()) == 3:
|
|
rc = int(m.groups()[0])
|
|
break
|
|
except Exception as e:
|
|
print('exception when exec {}'.format(cmd), e)
|
|
rc = -1
|
|
|
|
if rc > 0:
|
|
print('\n'.join(r_lines))
|
|
|
|
return rc
|
|
|
|
|
|
def check_iasl():
|
|
'''
|
|
check iasl installed
|
|
:return: True if iasl installed.
|
|
'''
|
|
try:
|
|
p_version = 'ASL+ Optimizing Compiler/Disassembler version'
|
|
min_version = 20190703
|
|
output = subprocess.check_output(['iasl', '-v']).decode('utf8')
|
|
if p_version in output:
|
|
try:
|
|
for line in output.split('\n'):
|
|
if line.find(p_version) >= 0:
|
|
version = int(line.split(p_version)[1].strip())
|
|
if version >= min_version:
|
|
return True
|
|
except:
|
|
pass
|
|
return False
|
|
elif 'command not found' in output:
|
|
return False
|
|
else:
|
|
print(output)
|
|
return False
|
|
except Exception as e:
|
|
print(e)
|
|
return False
|
|
|
|
|
|
def main(args):
|
|
|
|
board_etree = lxml.etree.parse(args.board)
|
|
scenario_etree = lxml.etree.parse(args.scenario)
|
|
|
|
scenario_name = common.get_node("//@scenario", scenario_etree)
|
|
|
|
if args.asl is None:
|
|
DEST_ACPI_PATH = os.path.join(VM_CONFIGS_PATH, 'scenarios', scenario_name)
|
|
else:
|
|
DEST_ACPI_PATH = os.path.join(common.SOURCE_ROOT_DIR, args.asl, 'scenarios', scenario_name)
|
|
if args.out is None:
|
|
hypervisor_out = os.path.join(common.SOURCE_ROOT_DIR, 'build', 'hypervisor')
|
|
else:
|
|
hypervisor_out = args.out
|
|
DEST_ACPI_BIN_PATH = os.path.join(hypervisor_out, 'acpi')
|
|
|
|
allocation_etree = lxml.etree.parse(os.path.join(hypervisor_out, 'configs', 'allocation.xml'))
|
|
|
|
if os.path.isdir(DEST_ACPI_BIN_PATH):
|
|
shutil.rmtree(DEST_ACPI_BIN_PATH)
|
|
|
|
if not check_iasl():
|
|
print("Please install iasl tool with version >= 20190703 from https://www.acpica.org/downloads before ACPI generation.")
|
|
return 1
|
|
|
|
for config in os.listdir(DEST_ACPI_PATH):
|
|
if os.path.isdir(os.path.join(DEST_ACPI_PATH, config)) and config.startswith('ACPI_VM'):
|
|
print('start to generate ACPI binary for {}'.format(config))
|
|
dest_vm_acpi_path = os.path.join(DEST_ACPI_PATH, config)
|
|
dest_vm_acpi_bin_path = os.path.join(DEST_ACPI_BIN_PATH, config)
|
|
os.makedirs(dest_vm_acpi_bin_path)
|
|
if asl_to_aml(dest_vm_acpi_path, dest_vm_acpi_bin_path):
|
|
return 1
|
|
aml_to_bin(dest_vm_acpi_path, dest_vm_acpi_bin_path, config+'.bin', board_etree, scenario_etree, allocation_etree)
|
|
|
|
return 0
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser(usage="python3 bin_gen.py --board [board] --scenario [scenario]"
|
|
"[ --out [output dir of acpi ASL code]]",
|
|
description="the tool to generate ACPI binary for Pre-launched VMs")
|
|
parser.add_argument("--board", required=True, help="the XML file summarizing characteristics of the target board")
|
|
parser.add_argument("--scenario", required=True, help="the XML file specifying the scenario to be set up")
|
|
parser.add_argument("--asl", default=None, help="the input folder to store the ACPI ASL code. ")
|
|
parser.add_argument("--out", default=None, help="the output folder to store the ACPI binary code. "
|
|
"If not specified, the path for the binary code is"
|
|
"build/hypervisor/acpi/")
|
|
|
|
args = parser.parse_args()
|
|
rc = main(args)
|
|
sys.exit(rc)
|