diff --git a/misc/acrn-config/target/README b/misc/acrn-config/target/README new file mode 100644 index 000000000..88fd0e927 --- /dev/null +++ b/misc/acrn-config/target/README @@ -0,0 +1,13 @@ +board_parser.py will collect all board related info and then generate a board info file for acrn-config host tool usage. + +usage: python3 board_parser.py [--out board_info_file] + +board_name : the name of board that run ACRN hypervisor, like apl-up2/nuc7i7dnb. It will be used as name of the board configurations folder which created by acrn-config host tool. +board_info_file : (optional) the name of board info file. if it is not specified, a name of .xml will be generated under ./out/ folder by default. + +Please run this script under native Linux environment with root privilege. + +OS requirement: + Release: Ubuntu 18.04+ or ClearLinux 30210+ + Tools: cpuid, rdmsr, lspci, dmidecode + kernel cmdline: "idle=nomwait intel_idle.max_cstate=0 intel_pstate=disable" diff --git a/misc/acrn-config/target/acpi.py b/misc/acrn-config/target/acpi.py new file mode 100644 index 000000000..40843fc51 --- /dev/null +++ b/misc/acrn-config/target/acpi.py @@ -0,0 +1,529 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import os +import sys +import subprocess +from collections import defaultdict +import dmar +import parser_lib + + +SYS_PATH = ['/proc/cpuinfo', '/sys/firmware/acpi/tables/', '/sys/devices/system/cpu/'] + +ACPI_OP = { + 'AML_ZERO_OP':0x00, + 'AML_ONE_OP':0x01, + 'AML_ALIAS_OP':0x06, + 'AML_NAME_OP':0x08, + 'AML_BYTE_OP':0x0a, + 'AML_WORD_OP':0x0b, + 'AML_DWORD_OP':0x0c, + 'AML_PACKAGE_OP':0x12, + 'AML_VARIABLE_PACKAGE_OP':0x13, + } + +SPACE_ID = { + 0:'SPACE_SYSTEM_MEMORY', + 1:'SPACE_SYSTEM_IO', + 2:'SPACE_PCI_CONFIG', + 3:'SPACE_Embedded_Control', + 4:'SPACE_SMBUS', + 10:'SPACE_PLATFORM_COMM', + 0x7F:'SPACE_FFixedHW', + } + +FACP_OFF = { + 'facs_addr':36, + 'reset_addr':116, + 'reset_value':128, + 'pm1a_evt':148, + 'pm1b_evt':160, + 'pm1a_cnt':172, + 'pm1b_cnt':184, + } + +class SxPkg: + """This is Sx state structure for power""" + def __init__(self): + # This is Sx state structure for power + self.val_pm1a = '' + self.val_pm1b = '' + self.reserved = '' + + def style_check_1(self): + """Style check if have public method""" + self.val_pm1a = '' + + def style_check_2(self): + """Style check if have public method""" + self.val_pm1a = '' + +class GasType: + """This is generic address structure for power""" + def __init__(self): + self.space_id_8b = 0 + self.bit_width_8b = 0 + self.bit_offset_8b = 0 + self.access_size_8b = 0 + self.address_64b = 0 + + def style_check_1(self): + """Style check if have public method""" + self.space_id_8b = '' + + def style_check_2(self): + """Style check if have public method""" + self.space_id_8b = '' + +class PxPkg: + """This is Px state structure for power""" + def __init__(self): + self.core_freq = 0 + self.power = 0 + self.trans_latency = 0 + self.bus_latency = 0 + self.control = 0 + self.status = 0 + + def style_check_1(self): + """Style check if have public method""" + self.power = '' + + def style_check_2(self): + """Style check if have public method""" + self.power = '' + +class ResetReg: + """This is Reset Registers meta data""" + def __init__(self): + self.reset_reg_addr = 0 + self.reset_reg_space_id = 0 + self.reset_reg_val = 0 + + def style_check_1(self): + """Style check if have public method""" + self.reset_reg_val = '' + + def style_check_2(self): + """Style check if have public method""" + self.reset_reg_val = '' + +DWORD_LEN = 4 +PACK_TYPE_LEN = 12 +WAKE_VECTOR_OFFSET_32 = 12 +WAKE_VECTOR_OFFSET_64 = 24 + +S3_PKG = SxPkg() +S5_PKG = SxPkg() +PackedGas = GasType +PackedCx = GasType + + +def store_cpu_info(sysnode, config): + """This will get CPU information from /proc/cpuifo""" + with open(sysnode, 'r') as f_node: + line = f_node.readline() + while line: + if len(line.split(':')) >= 2: + if line.split(':')[0].strip() == "model name": + model_name = line.split(':')[1].strip() + print('\t\t"{0}"'.format(model_name), file=config) + break + line = f_node.readline() + + +def write_reset_reg(space_id, rst_reg_addr, rst_reg_space_id, rst_reg_val, config): + """Write reset register info""" + print("\t{0}".format(""), file=config) + print("\t#define RESET_REGISTER_ADDRESS 0x{:0>2X}UL".format( + rst_reg_addr), file=config) + print("\t#define RESET_REGISTER_SPACE_ID {0}".format( + space_id[rst_reg_space_id]), file=config) + print("\t#define RESET_REGISTER_VALUE {0}U".format( + rst_reg_val), file=config) + print("\t{0}\n".format(""), file=config) + + +def get_vector_reset(sysnode, config): + """This will get reset reg value""" + reset_reg = ResetReg() + for key, offset in FACP_OFF.items(): + with open(sysnode, 'rb') as f_node: + f_node.seek(offset, 0) + if key == 'facs_addr': + packed_data = f_node.read(DWORD_LEN) + packed_data_32 = int.from_bytes(packed_data, 'little')+WAKE_VECTOR_OFFSET_32 + packed_data_64 = int.from_bytes(packed_data, 'little')+WAKE_VECTOR_OFFSET_64 + print("\t{0}".format(""), file=config) + print("\t#define WAKE_VECTOR_32 0x{:0>2X}UL".format( + packed_data_32), file=config) + print("\t#define WAKE_VECTOR_64 0x{:0>2X}UL".format( + packed_data_64), file=config) + print("\t{0}\n".format(""), file=config) + elif key == 'reset_addr': + packed_data = f_node.read(PACK_TYPE_LEN) + reset_reg.reset_reg_space_id = packed_data[0] + reset_reg.reset_reg_addr = int.from_bytes(packed_data[4:11], 'little') + elif key == 'reset_value': + packed_data = f_node.read(1) + reset_reg.reset_reg_val = hex(packed_data[0]) + + write_reset_reg(SPACE_ID, reset_reg.reset_reg_addr, reset_reg.reset_reg_space_id, + reset_reg.reset_reg_val, config) + + +def read_pm_sstate(sysnode, config): + """This will read Px state of power""" + get_vector_reset(sysnode, config) + print("\t{0}".format(""), file=config) + for key, offset in FACP_OFF.items(): + with open(sysnode, 'rb') as f_node: + f_node.seek(offset, 0) + + packed_data = f_node.read(PACK_TYPE_LEN) + PackedGas.space_id_8b = packed_data[0] + PackedGas.bit_width_8b = packed_data[1] + PackedGas.bit_offset_8b = packed_data[2] + PackedGas.access_size_8b = packed_data[3] + PackedGas.address_64b = int.from_bytes(packed_data[4:11], 'little') + + if key == 'pm1a_evt': + print("\t#define PM1A_EVT_SPACE_ID {0}".format( + SPACE_ID[PackedGas.space_id_8b]), file=config) + print("\t#define PM1A_EVT_BIT_WIDTH {0}U".format( + hex(PackedGas.bit_width_8b)), file=config) + print("\t#define PM1A_EVT_BIT_OFFSET {0}U".format( + hex(PackedGas.bit_offset_8b)), file=config) + print("\t#define PM1A_EVT_ADDRESS {0}UL".format( + hex(PackedGas.address_64b)), file=config) + print("\t#define PM1A_EVT_ACCESS_SIZE {0}U".format( + hex(PackedGas.access_size_8b)), file=config) + elif key == 'pm1a_cnt': + print("\t#define PM1A_CNT_SPACE_ID {0}".format( + SPACE_ID[PackedGas.space_id_8b]), file=config) + print("\t#define PM1A_CNT_BIT_WIDTH {0}U".format( + hex(PackedGas.bit_width_8b)), file=config) + print("\t#define PM1A_CNT_BIT_OFFSET {0}U".format( + hex(PackedGas.bit_offset_8b)), file=config) + print("\t#define PM1A_CNT_ADDRESS {0}UL".format( + hex(PackedGas.address_64b)), file=config) + print("\t#define PM1A_CNT_ACCESS_SIZE {0}U".format( + hex(PackedGas.access_size_8b)), file=config) + elif key == 'pm1b_evt': + print("\t#define PM1B_EVT_SPACE_ID {0}".format( + SPACE_ID[PackedGas.space_id_8b]), file=config) + print("\t#define PM1B_EVT_BIT_WIDTH {0}U".format( + hex(PackedGas.bit_width_8b)), file=config) + print("\t#define PM1B_EVT_BIT_OFFSET {0}U".format( + hex(PackedGas.bit_offset_8b)), file=config) + print("\t#define PM1B_EVT_ADDRESS {0}UL".format( + hex(PackedGas.address_64b)), file=config) + print("\t#define PM1B_EVT_ACCESS_SIZE {0}U".format( + hex(PackedGas.access_size_8b)), file=config) + elif key == 'pm1b_cnt': + print("\t#define PM1B_CNT_SPACE_ID {0}".format( + SPACE_ID[PackedGas.space_id_8b]), file=config) + print("\t#define PM1B_CNT_BIT_WIDTH {0}U".format( + hex(PackedGas.bit_width_8b)), file=config) + print("\t#define PM1B_CNT_BIT_OFFSET {0}U".format( + hex(PackedGas.bit_offset_8b)), file=config) + print("\t#define PM1B_CNT_ADDRESS {0}UL".format( + hex(PackedGas.address_64b)), file=config) + print("\t#define PM1B_CNT_ACCESS_SIZE {0}U".format( + hex(PackedGas.access_size_8b)), file=config) + print("\t{0}\n".format(""), file=config) + + +def if_sx_name(sx_name, f_node): + """If sx name in this field""" + need_break = need_continue = 0 + name_buf = f_node.read(4) + if not name_buf: + need_break = True + return (need_break, need_continue) + + try: + if name_buf.decode('ascii').find(sx_name) != -1: + pass + else: + need_continue = True + except ValueError: + need_continue = True + return (need_break, need_continue) + else: + pass + + return (need_break, need_continue) + + +def read_sx_locate(sx_name, f_node): + """Read the location of sx""" + need_continue = need_break = pkg_len = 0 + + (need_break, need_continue) = if_sx_name(sx_name, f_node) + + tmp_char = f_node.read(1) + if not tmp_char: + need_break = True + return (need_break, need_continue, pkg_len) + if hex(int.from_bytes(tmp_char, 'little')) != hex(ACPI_OP['AML_PACKAGE_OP']): + need_continue = True + return (need_break, need_continue, pkg_len) + + pkg_len = f_node.read(1) + if not pkg_len: + need_break = True + return (need_break, need_continue, pkg_len) + if int.from_bytes(pkg_len, 'little') < 5 or int.from_bytes(pkg_len, 'little') > 28: + need_continue = True + return (need_break, need_continue, pkg_len) + + return (need_break, need_continue, pkg_len) + + +def decode_sx_pkg(pkg_len, f_node): + """Parser and decode the sx pkg""" + pkg_val_pm1a = pkg_val_pm1b = pkg_val_resv = need_break = 0 + pkg_buf = f_node.read(int.from_bytes(pkg_len, 'little')) + if hex(pkg_buf[1]) == ACPI_OP['AML_ZERO_OP'] or \ + hex(pkg_buf[1]) == hex(ACPI_OP['AML_ONE_OP']): + pkg_val_pm1a = pkg_buf[1] + if hex(pkg_buf[2]) == hex(ACPI_OP['AML_ZERO_OP']) or \ + hex(pkg_buf[2]) == hex(ACPI_OP['AML_ONE_OP']): + pkg_val_pm1b = pkg_buf[2] + pkg_val_resv = pkg_buf[3:5] + elif hex(pkg_buf[2]) == hex(ACPI_OP['AML_BYTE_OP']): + pkg_val_pm1b = pkg_buf[3] + pkg_val_resv = pkg_buf[4:6] + else: + need_break = True + return (pkg_val_pm1a, pkg_val_pm1b, pkg_val_resv, need_break) + + elif hex(pkg_buf[1]) == hex(ACPI_OP['AML_BYTE_OP']): + pkg_val_pm1a = pkg_buf[2] + if hex(pkg_buf[3]) == hex(ACPI_OP['AML_ZERO_OP']) or \ + hex(pkg_buf[3]) == hex(ACPI_OP['AML_ONE_OP']): + pkg_val_pm1b = pkg_buf[3] + pkg_val_resv = pkg_buf[4:6] + elif hex(pkg_buf[3]) == hex(ACPI_OP['AML_BYTE_OP']): + pkg_val_pm1b = pkg_buf[4] + pkg_val_resv = pkg_buf[5:7] + else: + need_break = True + return (pkg_val_pm1a, pkg_val_pm1b, pkg_val_resv, need_break) + else: + need_break = True + + return (pkg_val_pm1a, pkg_val_pm1b, pkg_val_resv, need_break) + + +def read_pm_sdata(sysnode, sx_name, config): + """This will read pm Sx state of power""" + with open(sysnode, 'rb') as f_node: + while True: + inc = f_node.read(1) + if inc: + if hex(int.from_bytes(inc, 'little')) != hex(ACPI_OP['AML_NAME_OP']): + continue + + (need_break, need_continue, pkg_len) = read_sx_locate(sx_name, f_node) + if need_break: + break + if need_continue: + continue + + # decode sx pkg + (pkg_val_pm1a, pkg_val_pm1b, pkg_val_resv, need_break) =\ + decode_sx_pkg(pkg_len, f_node) + if need_break: + break + + else: + break + + if sx_name == '_S3_': + S3_PKG.val_pm1a = pkg_val_pm1a + S3_PKG.val_pm1b = pkg_val_pm1b + S3_PKG.reserved = pkg_val_resv + print("\t#define S3_PKG_VAL_PM1A {0}".format( + hex(S3_PKG.val_pm1a))+'U', file=config) + print("\t#define S3_PKG_VAL_PM1B {0}".format( + S3_PKG.val_pm1b)+'U', file=config) + print("\t#define S3_PKG_RESERVED {0}".format( + hex(int.from_bytes(S3_PKG.reserved, 'little')))+'U', file=config) + + if sx_name == "_S5_": + S5_PKG.val_pm1a = pkg_val_pm1a + S5_PKG.val_pm1b = pkg_val_pm1b + S5_PKG.reserved = pkg_val_resv + print("\t#define S5_PKG_VAL_PM1A {0}".format( + hex(S5_PKG.val_pm1a))+'U', file=config) + print("\t#define S5_PKG_VAL_PM1B {0}".format( + S5_PKG.val_pm1b)+'U', file=config) + print("\t#define S5_PKG_RESERVED {0}".format( + hex(int.from_bytes(S5_PKG.reserved, 'little')))+'U', file=config) + + +def store_cx_data(sysnode1, sysnode2, config): + """This will get Cx data of power and store it to PackedCx""" + i = 0 + state_cpus = {} + with open(sysnode1, 'r') as acpi_idle: + idle_driver = acpi_idle.read(32) + + if idle_driver.find("acpi_idle") == -1: + if idle_driver.find("intel_idle") == 0: + parser_lib.print_red("The tool need to run with acpi_idle driver, " + + "please add intel_idle.max_cstate=0 in kernel " + + "cmdline to fall back to acpi_idle driver", err=True) + else: + parser_lib.print_red("acpi_idle driver is not found.", err=True) + sys.exit(1) + + files = os.listdir(sysnode2) + for d_path in files: + if os.path.isdir(sysnode2+d_path): + state_cpus[d_path] = sysnode2+d_path + + state_cpus = sorted(state_cpus.keys()) + del state_cpus[0] + cpu_state = ['desc', 'latency', 'power'] + acpi_hw_type = ['HLT', 'MWAIT', 'IOPORT'] + + cx_state = defaultdict(dict) + for state in state_cpus: + i += 1 + for item in cpu_state: + cx_data_file = open(sysnode2+state+'/'+item, 'r') + cx_state[state][item] = cx_data_file.read().strip() + cx_state[state]['type'] = i + + if cx_state[state][cpu_state[0]].find(acpi_hw_type[0]) != -1 or \ + cx_state[state][cpu_state[0]].find(acpi_hw_type[1]) != -1: + PackedCx.space_id_8b = SPACE_ID[0x7F] + if cx_state[state][cpu_state[0]].find(acpi_hw_type[0]) != -1: + PackedCx.bit_width_8b = 0 + PackedCx.bit_offset_8b = 0 + PackedCx.access_size_8b = 0 + PackedCx.address_64b = 0 + else: + PackedCx.bit_width_8b = 1 + PackedCx.bit_offset_8b = 2 + PackedCx.access_size_8b = 1 + PackedCx.address_64b = cx_state[state][cpu_state[0]].split()[3] + elif cx_state[state][cpu_state[0]].find(acpi_hw_type[2]) != -1: + PackedCx.space_id_8b = SPACE_ID[1] + PackedCx.bit_width_8b = 8 + PackedCx.bit_offset_8b = 0 + PackedCx.access_size_8b = 0 + PackedCx.address_64b = cx_state[state][cpu_state[0]].split()[2] + print("\t\t{{{{{}, 0x{:0>2X}U, 0x{:0>2X}U, 0x{:0>2X}U, ".format( + PackedCx.space_id_8b, PackedCx.bit_width_8b, PackedCx.bit_offset_8b, + PackedCx.access_size_8b), file=config, end="") + print("0x{:0>2X}UL}}, 0x{:0>2X}U, 0x{:0>2X}U, 0x{:0>2X}U}},".format( + int(str(PackedCx.address_64b), 16), + cx_state[state]['type'], int(cx_state[state][cpu_state[1]]), + int(cx_state[state][cpu_state[2]])), file=config) + + +def store_px_data(sysnode, config): + """This will get Px data of power and store it to px data""" + px_tmp = PxPkg() + px_data = {} + with open(sysnode+'cpu0/cpufreq/scaling_driver', 'r') as f_node: + freq_driver = f_node.read() + if freq_driver.find("acpi-cpufreq") == -1: + if freq_driver.find("intel_pstate") == 0: + parser_lib.print_red("The tool need to run with acpi_cpufreq driver, " + + "please add intel_pstate=disable in kernel cmdline " + + "to fall back to acpi-cpufreq driver.", err=True) + else: + parser_lib.print_red("acpi-cpufreq driver is not found.", err=True) + sys.exit(1) + + try: + with open(sysnode+'cpufreq/boost', 'r') as f_node: + boost = f_node.read() + except IOError: + boost = 0 + parser_lib.print_yel("CPU turbo is not enabled!") + + with open(sysnode + 'cpu0/cpufreq/scaling_available_frequencies', 'r') as f_node: + freqs = f_node.read() + with open(sysnode + 'cpu0/cpufreq/cpuinfo_transition_latency') as f_node: + latency = int(f_node.read().strip()) + latency = latency//1000 + + i = 0 + p_cnt = 0 + for freq in freqs.split(): + if boost != 0 and i == 0: + try: + subprocess.check_call('/usr/sbin/rdmsr 0x1ad', shell=True, stdout=subprocess.PIPE) + except subprocess.CalledProcessError: + parser_lib.print_red("MSR 0x1ad not support in this platform!", err=True) + sys.exit(1) + + res = subprocess.Popen('/usr/sbin/rdmsr 0x1ad', shell=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) + result = res.stdout.readline().strip() + #max_ratio_cpu = result[-2:] + ctl_state = int(result[-2:], 16) << 8 + i += 1 + else: + ctl_state = int(freq)//100000 << 8 + + px_tmp.core_freq = int(int(freq) / 1000) + px_tmp.power = 0 + px_tmp.trans_latency = latency + px_tmp.bus_latency = latency + px_tmp.control = ctl_state + px_tmp.status = ctl_state + px_data[freq] = px_tmp + print("\t\t{{0x{:0>2X}UL, 0x{:0>2X}UL, 0x{:0>2X}UL, ".format( + px_data[freq].core_freq, px_data[freq].power, + px_data[freq].trans_latency), file=config, end="") + print("0x{:0>2X}UL, 0x{:0>6X}UL, 0x{:0>6X}UL}}, /* P{} */".format( + px_data[freq].bus_latency, px_data[freq].control, + px_data[freq].status, p_cnt), file=config) + + p_cnt += 1 + + +def gen_acpi_info(board_fp): + """This will parser the sys node form SYS_PATH and generate ACPI info""" + read_pm_sstate(SYS_PATH[1] + 'FACP', board_fp) + + print("{0}".format("\t"), file=board_fp) + read_pm_sdata(SYS_PATH[1] + 'DSDT', '_S3_', board_fp) + print("{0}".format("\t\n"), file=board_fp) + + print("{0}".format("\t"), file=board_fp) + read_pm_sdata(SYS_PATH[1] + 'DSDT', '_S5_', board_fp) + print("{0}".format("\t\n"), file=board_fp) + + print("{0}".format("\t"), file=board_fp) + dmar.write_dmar_data(SYS_PATH[1] + 'DMAR', board_fp) + print("{0}".format("\t\n"), file=board_fp) + + print("{0}".format("\t"), file=board_fp) + store_cpu_info(SYS_PATH[0], board_fp) + print("{0}".format("\t\n"), file=board_fp) + + print("{0}".format("\t"), file=board_fp) + store_cx_data(SYS_PATH[2]+'cpuidle/current_driver', SYS_PATH[2]+'cpu0/cpuidle/', board_fp) + print("{0}".format("\t\n"), file=board_fp) + + print("{0}".format("\t"), file=board_fp) + store_px_data(SYS_PATH[2], board_fp) + print("{0}".format("\t\n"), file=board_fp) + + +def generate_info(board_file): + """This will generate ACPI info from board file""" + # Generate board info + with open(board_file, 'a+') as board_info: + gen_acpi_info(board_info) diff --git a/misc/acrn-config/target/board_parser.py b/misc/acrn-config/target/board_parser.py new file mode 100755 index 000000000..99e76100b --- /dev/null +++ b/misc/acrn-config/target/board_parser.py @@ -0,0 +1,142 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import os +import sys +import shutil +import argparse +import subprocess +import parser_lib +import pci_dev +import dmi +import acpi +import clos + +OUTPUT = "./out/" +PY_CACHE = "__pycache__" + +# This file store information which query from hw board +BIN_LIST = ['cpuid', 'rdmsr', 'lspci', ' dmidecode'] +PCI_IDS = ["/usr/share/hwdata/pci.ids", "/usr/share/misc/pci.ids"] + +CPU_VENDOR = "GenuineIntel" + + +def check_permission(): + """Check if it is root permission""" + if os.getuid(): + parser_lib.print_red("You need run with sudo!") + sys.exit(1) + + +def native_check(): + """Check if this is natvie os""" + cmd = "cpuid -r -l 0x01" + res = parser_lib.cmd_excute(cmd) + while True: + line = parser_lib.decode_stdout(res) + + if line: + + if len(line.split()) <= 2: + continue + + reg_value = line.split()[4].split('=')[1] + break + + return int(reg_value, 16) & 0x80000000 == 0 + + +def vendor_check(): + """Check the CPU vendor""" + with open("/proc/cpuinfo", 'r') as f_node: + while True: + line = f_node.readline() + if len(line.split(':')) == 2: + if line.split(':')[0].strip() == "vendor_id": + vendor_name = line.split(':')[1].strip() + return vendor_name != CPU_VENDOR + + +def check_env(): + """Check if there is appropriate environment on this system""" + if os.path.exists(PY_CACHE): + shutil.rmtree(PY_CACHE) + + # check cpu vendor id + if vendor_check(): + parser_lib.print_red("Please run this tools on {}!".format(CPU_VENDOR)) + sys.exit(1) + + # check if required tools are exists + 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: + parser_lib.print_yel("'{}' not found, please install it!".format(excute)) + sys.exit(1) + + if excute == 'cpuid': + res = subprocess.Popen("cpuid -v", + shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, close_fds=True) + line = res.stdout.readline().decode('ascii') + version = line.split()[2] + if int(version) < 20170122: + parser_lib.print_yel("Need CPUID version >= 20170122") + sys.exit(1) + + + if not native_check(): + parser_lib.print_red("Please run this tools on natvie OS!") + sys.exit(1) + + if not os.path.exists(PCI_IDS[0]) and not os.path.exists(PCI_IDS[1]): + parser_lib.print_yel("pci.ids not found, please make sure lspci is installed correctly!") + sys.exit(1) + + if os.path.exists(OUTPUT): + shutil.rmtree(OUTPUT) + + +if __name__ == '__main__': + check_permission() + + check_env() + + # arguments to parse + PARSER = argparse.ArgumentParser(usage='%(prog)s [--out board_info_file]') + PARSER.add_argument('board_name', help=": the name of board that run ACRN hypervisor") + PARSER.add_argument('--out', help=": the name of board info file.") + ARGS = PARSER.parse_args() + + if not ARGS.out: + os.makedirs(OUTPUT) + BOARD_INFO = OUTPUT + ARGS.board_name + ".xml" + else: + BOARD_INFO = ARGS.out + + with open(BOARD_INFO, 'w+') as f: + print(''.format(ARGS.board_name), file=f) + + # Get bios and base board info and store to board info + dmi.generate_info(BOARD_INFO) + + # Get pci devicse table and store pci info to board info + pci_dev.generate_info(BOARD_INFO) + + # Generate board info + acpi.generate_info(BOARD_INFO) + + # Generate clos info + clos.generate_info(BOARD_INFO) + + with open(BOARD_INFO, 'a+') as f: + print("", file=f) + + print("{} is generaged successfully!".format(BOARD_INFO)) diff --git a/misc/acrn-config/target/clos.py b/misc/acrn-config/target/clos.py new file mode 100644 index 000000000..15400c6f1 --- /dev/null +++ b/misc/acrn-config/target/clos.py @@ -0,0 +1,81 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import parser_lib + +CACHE_TYPE = { + "L2":4, + "L3":2 + } + + +def execute(cmd, reg): + """Execute the cmd""" + cache_t = '' + + res = parser_lib.cmd_excute(cmd) + if reg == "ebx": + idx = 3 + + if reg == "edx": + idx = 5 + + while True: + line = parser_lib.decode_stdout(res) + + if not line: + break + + if len(line.split()) <= 2: + continue + + reg_value = line.split()[idx].split('=')[1] + + if reg == "ebx": + if int(reg_value, 16) & CACHE_TYPE['L2'] != 0: + cache_t = "L2" + break + elif int(reg_value, 16) & CACHE_TYPE['L3'] != 0: + cache_t = "L3" + break + else: + cache_t = False + break + elif reg == "edx": + cache_t = int(reg_value, 16) + 1 + break + + return cache_t + + +def get_clos_info(): + """Get clos max and clos cache type""" + clos_max = 0 + clos_cache = False + cmd = "cpuid -r -l 0x10" + clos_cache = execute(cmd, "ebx") + + if clos_cache == "L2": + cmd = "cpuid -r -l 0x10 --subleaf 2" + elif clos_cache == "L3": + cmd = "cpuid -r -l 0x10 --subleaf 1" + else: + clos_max = 0 + parser_lib.print_yel("CLOS is not supported!") + return (clos_cache, clos_max) + + clos_max = execute(cmd, "edx") + + return (clos_cache, clos_max) + +def generate_info(board_info): + """Generate clos information""" + (clos_cache, clos_max) = get_clos_info() + + with open(board_info, 'a+') as board_fp: + print("\t", file=board_fp) + print("\tclos supported by cache:{}".format(clos_cache), file=board_fp) + print("\tclos max:{}".format(clos_max), file=board_fp) + print("\t\n", file=board_fp) diff --git a/misc/acrn-config/target/dmar.py b/misc/acrn-config/target/dmar.py new file mode 100644 index 000000000..fc348dd77 --- /dev/null +++ b/misc/acrn-config/target/dmar.py @@ -0,0 +1,364 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import ctypes + +ACPI_DMAR_TYPE = { + 'ACPI_DMAR_TYPE_HARDWARE_UNIT':0, + 'ACPI_DMAR_TYPE_RESERVED_MEMORY':1, + 'ACPI_DMAR_TYPE_ROOT_ATS':2, + 'ACPI_DMAR_TYPE_HARDWARE_AFFINITY':3, + 'ACPI_DMAR_TYPE_NAMESPACE':4, + 'ACPI_DMAR_TYPE_RESERVED':5, + } + +ACPI_DEV_SCOPE_TYPE = { + 'ACPI_DMAR_SCOPE_TYPE_NOT_USED':0, + 'ACPI_DMAR_SCOPE_TYPE_ENDPOINT':1, + 'ACPI_DMAR_SCOPE_TYPE_BRIDGE':2, + 'ACPI_DMAR_SCOPE_TYPE_IOAPIC':3, + 'ACPI_DMAR_SCOPE_TYPE_HPET':4, + 'ACPI_DMAR_SCOPE_TYPE_NAMESPACE':5, + 'ACPI_DMAR_SCOPE_TYPE_RESERVED':6, + } + +class DmarHeader(ctypes.Structure): + """DMAR Header""" + _pack_ = 1 + _fields_ = [ + ('signature', ctypes.c_char*4), + ('length', ctypes.c_uint32), + ('revision', ctypes.c_ubyte), + ('checksum', ctypes.c_ubyte), + ('oem_id', ctypes.c_char*6), + ('oem_table_id', ctypes.c_char*8), + ('oem_revision', ctypes.c_uint32), + ('asl_compiler_id', ctypes.c_char*4), + ('asl_compiler_revision', ctypes.c_uint32), + ('host_addr_width', ctypes.c_ubyte), + ('flags', ctypes.c_ubyte), + ('reserved', ctypes.c_ubyte*10), + ] + + def style_check_1(self): + """Style check if have public method""" + self._pack_ = 0 + + def style_check_2(self): + """Style check if have public method""" + self._pack_ = 0 + + +class DmarSubtblHeader(ctypes.Structure): + """DMAR Sub Table Header""" + _pack_ = 1 + _fields_ = [ + ('type', ctypes.c_uint16), + ('length', ctypes.c_uint16), + ] + + def style_check_1(self): + """Style check if have public method""" + self._pack_ = 0 + + def style_check_2(self): + """Style check if have public method""" + self._pack_ = 0 + + +class DmarDevScope(ctypes.Structure): + """DMAR Device Scope""" + _pack_ = 1 + _fields_ = [ + ('entry_type', ctypes.c_uint8), + ('scope_length', ctypes.c_uint8), + ('reserved', ctypes.c_uint16), + ('enumeration_id', ctypes.c_uint8), + ('bus', ctypes.c_uint8), + ] + + def style_check_1(self): + """Style check if have public method""" + self._pack_ = 0 + + def style_check_2(self): + """Style check if have public method""" + self._pack_ = 0 + + +class DmarHwUnit(ctypes.Structure): + """DMAR Hardware Unit""" + _pack_ = 1 + _fields_ = [ + ('sub_header', DmarSubtblHeader), + ('flags', ctypes.c_uint8), + ('reserved', ctypes.c_uint8), + ('segment', ctypes.c_uint16), + ('address', ctypes.c_uint64), + ] + + def style_check_1(self): + """Style check if have public method""" + self._pack_ = 0 + + def style_check_2(self): + """Style check if have public method""" + self._pack_ = 0 + + +class DevScopePath(ctypes.Structure): + """DEVICE Scope Path""" + _pack_ = 1 + _fields_ = [ + ("device", ctypes.c_uint8), + ("function", ctypes.c_uint8), + ] + + def style_check_1(self): + """Style check if have public method""" + self._pack_ = 0 + + def style_check_2(self): + """Style check if have public method""" + self._pack_ = 0 + + +def map_file(sysnode): + """Map sys node to memory address""" + data = open(sysnode, 'rb').read() + buf = ctypes.create_string_buffer(data, len(data)) + addr = ctypes.addressof(buf) + + return addr + +class DmarHwList: + """DMAR HW List""" + def __init__(self): + self.hw_segment_list = [] + self.hw_flags_list = [] + self.hw_address_list = [] + self.hw_ignore = {} + + def style_check_1(self): + """Style check if have public method""" + self.hw_ignore = {} + + def style_check_2(self): + """Style check if have public method""" + self.hw_ignore = {} + + +class DmarDevList: + """DMAR DEV List""" + def __init__(self): + self.dev_scope_cnt_list = [] + self.dev_bus_list = [] + self.dev_path_list = [] + self.dev_ioapic = {} + + def style_check_1(self): + """Style check if have public method""" + self.dev_bus_list = [] + + def style_check_2(self): + """Style check if have public method""" + self.dev_bus_list = [] + + +class DmarTbl: + """DMAR TBL""" + def __init__(self): + self.sub_tbl_offset = 0 + self.dmar_drhd = 0 + self.dmar_dev_scope = 0 + self.dev_scope_offset = 0 + self.dev_scope_cnt = 0 + self.path_offset = 0 + + def style_check_1(self): + """Style check if have public method""" + self.path_offset = 0 + + def style_check_2(self): + """Style check if have public method""" + self.path_offset = 0 + + +class PathDevFun: + """Path Device Function meta data""" + def __init__(self): + self.path = 0 + self.device = 0 + self.function = 0 + + def style_check_1(self): + """Style check if have public method""" + self.path = 0 + + def style_check_2(self): + """Style check if have public method""" + self.path = 0 + + +def walk_pci_bus(tmp_pdf, dmar_tbl, dmar_hw_list, n_cnt, drhd_cnt): + """Walk Pci bus""" + while n_cnt: + scope_path = DevScopePath.from_address(dmar_tbl.path_offset) + tmp_pdf.device = scope_path.device + tmp_pdf.function = scope_path.function + tmp_pdf.path = (((tmp_pdf.device & 0x1F) << 3) | ((tmp_pdf.function & 0x7))) + dmar_tbl.path_offset += ctypes.sizeof(DevScopePath) + n_cnt -= 1 + + # this not support to warning if no dedicated iommu for gpu + if ((dmar_tbl.dmar_drhd.segment << 16) | ( + dmar_tbl.dmar_dev_scope.bus << 8) | scope_path.function) == 0x10: + dmar_hw_list.hw_ignore[drhd_cnt] = 'true' + else: + dmar_hw_list.hw_ignore[drhd_cnt] = 'false' + + return (tmp_pdf, dmar_tbl, dmar_hw_list) + + +def walk_dev_scope(dmar_tbl, dmar_dev_list, dmar_hw_list, drhd_cnt): + """Walk device scope""" + dmar_tbl.dev_scope_offset = dmar_tbl.sub_tbl_offset + ctypes.sizeof(DmarHwUnit) + scope_end = dmar_tbl.dev_scope_offset + dmar_tbl.dmar_drhd.sub_header.length + dmar_tbl.dev_scope_cnt = 0 + + while dmar_tbl.dev_scope_offset < scope_end: + dmar_tbl.dmar_dev_scope = DmarDevScope.from_address(dmar_tbl.dev_scope_offset) + if dmar_tbl.dmar_dev_scope.scope_length <= 0: + break + if dmar_tbl.dmar_dev_scope.entry_type != \ + ACPI_DEV_SCOPE_TYPE['ACPI_DMAR_SCOPE_TYPE_NOT_USED'] and \ + dmar_tbl.dmar_dev_scope.entry_type < \ + ACPI_DEV_SCOPE_TYPE['ACPI_DMAR_SCOPE_TYPE_RESERVED']: + dmar_tbl.dev_scope_cnt += 1 + + # path offset is in end of device spcope + dmar_tbl.path_offset = dmar_tbl.dev_scope_offset + ctypes.sizeof(DmarDevScope) + # walk the pci bus with path deep, and find the {Device,Function} + tmp_pdf = PathDevFun() + n_cnt = (dmar_tbl.dmar_dev_scope.scope_length - ctypes.sizeof(DmarDevScope)) // 2 + (tmp_pdf, dmar_tbl, dmar_hw_list) = walk_pci_bus(tmp_pdf, dmar_tbl, dmar_hw_list, + n_cnt, drhd_cnt) + dmar_dev_list.dev_bus_list.append(dmar_tbl.dmar_dev_scope.bus) + dmar_dev_list.dev_path_list.append(tmp_pdf.path) + + # if the scope entry type is ioapic, should address enumeration id + if dmar_tbl.dmar_dev_scope.entry_type ==\ + ACPI_DEV_SCOPE_TYPE['ACPI_DMAR_SCOPE_TYPE_IOAPIC']: + dmar_dev_list.dev_ioapic[drhd_cnt] = dmar_tbl.dmar_dev_scope.enumeration_id + + dmar_tbl.dev_scope_offset += dmar_tbl.dmar_dev_scope.scope_length + + return (dmar_tbl, dmar_dev_list, dmar_hw_list) + + +def walk_dmar_table(dmar_tbl, dmar_hw_list, dmar_dev_list, sysnode): + """Walk dmar table and get information""" + data = open(sysnode, 'rb').read() + buf = ctypes.create_string_buffer(data, len(data)) + addr = ctypes.addressof(buf) + + # contain the dmar tbl_header + dmar_tbl_header = DmarHeader.from_address(addr) + + # cytpes.c_int16.from_address(addr) reade int16 from ad1 + # in end of tbl header is remapping structure(DRHD/sub tbl) + dmar_tbl.sub_tbl_offset = addr + ctypes.sizeof(DmarHeader) + drhd_cnt = 0 + while True: + sub_dmar = DmarSubtblHeader.from_address(dmar_tbl.sub_tbl_offset) + sub_dmar_type = sub_dmar.type + sub_dmar_len = sub_dmar.length + + if dmar_tbl.sub_tbl_offset - addr >= dmar_tbl_header.length: + break + + if sub_dmar_type != ACPI_DMAR_TYPE['ACPI_DMAR_TYPE_HARDWARE_UNIT']: + dmar_tbl.sub_tbl_offset += sub_dmar.length + continue + + # get one DRHD type in sub table + dmar_tbl.dmar_drhd = DmarHwUnit.from_address(dmar_tbl.sub_tbl_offset) + dmar_hw_list.hw_segment_list.append(dmar_tbl.dmar_drhd.segment) + dmar_hw_list.hw_flags_list.append(dmar_tbl.dmar_drhd.flags) + dmar_hw_list.hw_address_list.append(dmar_tbl.dmar_drhd.address) + + # in end of DRHD/sub tbl header is devscope + (dmar_tbl, dmar_dev_list, dmar_hw_list) = walk_dev_scope( + dmar_tbl, dmar_dev_list, dmar_hw_list, drhd_cnt) + + dmar_dev_list.dev_scope_cnt_list.append(dmar_tbl.dev_scope_cnt) + drhd_cnt += 1 + dmar_tbl.sub_tbl_offset += sub_dmar_len + + return (dmar_tbl, dmar_hw_list, dmar_dev_list, drhd_cnt) + + +def write_dmar_data(sysnode, config): + """Write the DMAR data to board info""" + + dmar_hw_list = DmarHwList() + dmar_dev_list = DmarDevList() + dmar_tbl = DmarTbl() + + (dmar_tbl, dmar_hw_list, dmar_dev_list, drhd_cnt) = walk_dmar_table( + dmar_tbl, dmar_hw_list, dmar_dev_list, sysnode) + + # num drhd and scope are hard coded + drhd_num = 4 + scope_num = 4 + # padding dev_scope_cnt_list + j = 0 + if len(dmar_dev_list.dev_scope_cnt_list) < scope_num: + for j in range(scope_num - dmar_dev_list.dev_scope_cnt_list[j]): + dmar_dev_list.dev_scope_cnt_list.append(0) + + # padding dev_bus_list/dev_path_list + for i in range(drhd_num): + for scope_level in range(scope_num - dmar_dev_list.dev_scope_cnt_list[i]): + dmar_dev_list.dev_path_list.insert(i * 4 + dmar_dev_list.dev_scope_cnt_list[i], 0) + dmar_dev_list.dev_bus_list.insert(i * 4 + dmar_dev_list.dev_scope_cnt_list[i], 0) + scope_level = scope_level + + # padding the drhd count from hard code drhd_num + for i in range(drhd_num - drhd_cnt): + dmar_dev_list.dev_scope_cnt_list.append(0) + dmar_hw_list.hw_segment_list.append(0) + dmar_hw_list.hw_flags_list.append(0) + dmar_hw_list.hw_address_list.append(0) + dmar_hw_list.hw_ignore[drhd_cnt + i] = 'false' + + # output the DRHD macro + print("\t#define DRHD_COUNT {0}U".format(drhd_cnt), file=config) + for drhd_hw_i in range(drhd_num): + print("\t#define DRHD"+str(drhd_hw_i)+"_DEV_CNT {0}U".format( + dmar_dev_list.dev_scope_cnt_list[drhd_hw_i]), file=config) + print("\t#define DRHD"+str(drhd_hw_i)+"_SEGMENT {0}U".format( + dmar_hw_list.hw_segment_list[drhd_hw_i]), file=config) + print("\t#define DRHD"+str(drhd_hw_i)+"_FLAGS {0}U".format( + dmar_hw_list.hw_flags_list[drhd_hw_i]), file=config) + print("\t#define DRHD"+str(drhd_hw_i)+"_REG_BASE 0x{:0>2X}UL".format( + dmar_hw_list.hw_address_list[drhd_hw_i]), file=config) + if drhd_hw_i in dmar_hw_list.hw_ignore.keys(): + print("\t#define DRHD"+str(drhd_hw_i)+"_IGNORE {0}".format( + dmar_hw_list.hw_ignore[drhd_hw_i]), file=config) + for dev_scope_i in range(scope_num): + print("\t#define DRHD"+str(drhd_hw_i)+"_DEVSCOPE"+str(dev_scope_i), + file=config, end="") + print("_BUS {0}U".format(hex( + dmar_dev_list.dev_bus_list[drhd_hw_i * scope_num + dev_scope_i])), + file=config) + print("\t#define DRHD"+str(drhd_hw_i)+"_DEVSCOPE"+str(dev_scope_i), + file=config, end="") + print("_PATH {0}U".format(hex( + dmar_dev_list.dev_path_list[drhd_hw_i * scope_num + dev_scope_i])), + file=config) + if drhd_hw_i in dmar_dev_list.dev_ioapic.keys(): + print("\t#define DRHD"+str(drhd_hw_i)+"_IOAPIC_ID {0}U".format( + dmar_dev_list.dev_ioapic[drhd_hw_i]), file=config) diff --git a/misc/acrn-config/target/dmi.py b/misc/acrn-config/target/dmi.py new file mode 100644 index 000000000..7f1b393ae --- /dev/null +++ b/misc/acrn-config/target/dmi.py @@ -0,0 +1,20 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import parser_lib + +CMDS = { + 'BIOS_INFO':"dmidecode -t 0", + 'BASE_BOARD_INFO':"dmidecode -t 2", + } + + +def generate_info(board_info): + """Get bios and base board information""" + with open(board_info, 'a+') as config: + parser_lib.dump_excute(CMDS['BIOS_INFO'], 'BIOS_INFO', config) + print("", file=config) + parser_lib.dump_excute(CMDS['BASE_BOARD_INFO'], 'BASE_BOARD_INFO', config) + print("", file=config) diff --git a/misc/acrn-config/target/parser_lib.py b/misc/acrn-config/target/parser_lib.py new file mode 100644 index 000000000..9253b8551 --- /dev/null +++ b/misc/acrn-config/target/parser_lib.py @@ -0,0 +1,106 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import os +import subprocess + +BIOS_INFO_KEY = ['BIOS Information', 'Vendor:', 'Version:', 'Release Date:', 'BIOS Revision:'] + +BASE_BOARD_KEY = ['Base Board Information', 'Manufacturer:', 'Product Name:', 'Version:'] + + +def check_dmi(): + """Check if this tool run on native os""" + return os.path.exists("/sys/firmware/dmi") + + +def print_yel(msg, warn=False): + """Print the msg wiht color of yellow""" + if warn: + print("\033[1;33mWarning\033[0m:"+msg) + else: + print("\033[1;33m{0}\033[0m".format(msg)) + + +def print_red(msg, err=False): + """Print the msg wiht color of red""" + if err: + print("\033[1;31mError\033[0m:"+msg) + else: + print("\033[1;31m{0}\033[0m".format(msg)) + + +def decode_stdout(resource): + """Decode stdout""" + line = resource.stdout.readline().decode('ascii') + return line + + +def handle_hw_info(line, hw_info): + """handle the hardware information""" + for board_line in hw_info: + if board_line == " ".join(line.split()[0:1]) or \ + board_line == " ".join(line.split()[0:2]) or \ + board_line == " ".join(line.split()[0:3]): + return True + return False + + +def handle_pci_dev(line): + """Handle if it is pci line""" + if "Region" in line and "Memory at" in line: + return True + + if line != '\n': + if line.split()[0][2:3] == ':' and line.split()[0][5:6] == '.': + return True + + return False + + +def cmd_excute(cmd): + """Excute cmd and retrun raw""" + res = subprocess.Popen(cmd, shell=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) + + return res + + +def dump_excute(cmd, desc, config): + """Execute cmd and get information""" + val_dmi = check_dmi() + print("\t<{0}>".format(desc), file=config) + + if not val_dmi and "dmidecode" in cmd: + print("\t\t".format(desc), file=config) + return + + res = cmd_excute(cmd) + while True: + line = res.stdout.readline().decode('ascii') + + if not line: + break + + if desc == "PCI_DEVICE": + if "prog-if" in line: + line = line.rsplit('(', 1)[0] + '\n' + ret = handle_pci_dev(line) + if not ret: + continue + + if desc == "BIOS_INFO": + ret = handle_hw_info(line, BIOS_INFO_KEY) + if not ret: + continue + + if desc == "BASE_BOARD_INFO": + ret = handle_hw_info(line, BASE_BOARD_KEY) + if not ret: + continue + + print("\t{}".format(line.strip()), file=config) + + print("\t".format(desc), file=config) diff --git a/misc/acrn-config/target/pci_dev.py b/misc/acrn-config/target/pci_dev.py new file mode 100644 index 000000000..cd31a8ce6 --- /dev/null +++ b/misc/acrn-config/target/pci_dev.py @@ -0,0 +1,20 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import parser_lib + +CMDS = { + 'PCI_DEVICE':"lspci -vv", + 'PCI_VID_PID':"lspci -n", + } + + +def generate_info(board_info): + """Get the pci info""" + with open(board_info, 'a+') as config: + parser_lib.dump_excute(CMDS['PCI_DEVICE'], 'PCI_DEVICE', config) + print("", file=config) + parser_lib.dump_excute(CMDS['PCI_VID_PID'], 'PCI_VID_PID', config) + print("", file=config)