acrn-hypervisor/misc/config_tools/board_config/board_c.py
Wu Zhou 41b2d6baee config-tools: generate CPU frequency limits
This patch is to generates frequency limits for each CPU, as a set of
data structure in hypervisor .c code.

With the frequency limits data, the hypervisor performance manager does
not have to deal with the CPU/board info. It just choose the
highest/lowest/guaranteed performance level and performance/nominal
p-state, and use them to construct HWP_REQUEST/PERF_CTL reg value.

How are frequency limits decided:
  - For CPUs in standard VMs, frequency limits are just decided by
    CPU/board info.
  - For CPUs assigned to RTVMs, we want certainty in latency, so just
    set its frequency to nominal/guaranteed by letting highest=lowest.
  - In some cases, CPUs are sharing frequency on hardware level
    (e.g. ADL e-cores in group of 4). This is described as _PSD in ACPI
    spec, or 'frequency domain' in Linux cpufreq driver. Thoese CPUs'
    frequency are linked together. If one of them are running RTVM,
    all other CPUs in the domain should be set to the same frequency.

Tracked-On: #8168
Signed-off-by: Wu Zhou <wu.zhou@intel.com>
Reviewed-by: Junjie Mao <junjie.mao@intel.com>
2022-10-08 11:13:21 +08:00

516 lines
22 KiB
Python

# Copyright (C) 2019-2022 Intel Corporation.
#
# SPDX-License-Identifier: BSD-3-Clause
#
import sys
import enum
import board_cfg_lib
import common
import lxml.etree
import os
class RDT(enum.Enum):
L2 = 0
L3 = 1
MBA = 2
INCLUDE_HEADER = """
#include <asm/board.h>
#include <asm/vtd.h>
#include <asm/msr.h>
#include <asm/rdt.h>
#include <pci.h>
#include <misc_cfg.h>
"""
MSR_IA32_L2_MASK_BASE = 0x00000D10
MSR_IA32_L2_MASK_END = 0x00000D4F
MSR_IA32_L3_MASK_BASE = 0x00000C90
MSR_IA32_L3_MASK_END = 0x00000D0F
def gen_dmar_structure(config):
"""Generate dmar structure information"""
dmar_info_lines = board_cfg_lib.get_info(common.BOARD_INFO_FILE, "<DRHD_INFO>", "</DRHD_INFO>")
drhd_cnt = 0
drhd_dev_scope_cnt = []
dev_scope_type = []
if not dmar_info_lines:
print("\n#ifndef CONFIG_ACPI_PARSE_ENABLED", file=config)
print("#error \"DMAR info is not available, please set ACPI_PARSE_ENABLED to y. \\", file=config)
print("\tOr use acrn-config tool to generate platform DMAR info.\"", file=config)
print("#endif\n", file=config)
print("struct dmar_info plat_dmar_info;\n", file=config)
return
# parse to get DRHD count and dev scope count
for dmar_line in dmar_info_lines:
if "DRHD_COUNT" in dmar_line and not drhd_cnt:
drhd_cnt = int(dmar_line.split()[2].strip('U'))
for i_cnt in range(drhd_cnt):
for dmar_line in dmar_info_lines:
dev_scope_cnt_str = "DRHD{}_DEV_CNT".format(i_cnt)
if dev_scope_cnt_str in dmar_line:
tmp_dev_scope_cnt = int(dmar_line.split()[2].strip('U'), 16)
drhd_dev_scope_cnt.append(tmp_dev_scope_cnt)
# gen dmar structure information
for i_drhd_cnt in range(drhd_cnt):
dev_cnt = drhd_dev_scope_cnt[i_drhd_cnt]
print("static struct dmar_dev_scope drhd{}_dev_scope[DRHD{}_DEV_CNT] = {{".format(
i_drhd_cnt, i_drhd_cnt), file=config)
for i_dev_cnt in range(dev_cnt):
print("\t{", file=config)
print("\t\t.type = DRHD{}_DEVSCOPE{}_TYPE,".format(i_drhd_cnt, i_dev_cnt), file=config)
print("\t\t.id = DRHD{}_DEVSCOPE{}_ID,".format(i_drhd_cnt, i_dev_cnt), file=config)
print("\t\t.bus = DRHD{}_DEVSCOPE{}_BUS,".format(i_drhd_cnt, i_dev_cnt), file=config)
print("\t\t.devfun = DRHD{}_DEVSCOPE{}_PATH,".format(i_drhd_cnt, i_dev_cnt), file=config)
print("\t},", file=config)
print("};", file=config)
print("", file=config)
print("static struct dmar_drhd drhd_info_array[DRHD_COUNT] = {", file=config)
for i_drhd_cnt in range(drhd_cnt):
print("\t{", file=config)
print("\t\t.dev_cnt = DRHD{}_DEV_CNT,".format(i_drhd_cnt), file=config)
print("\t\t.segment = DRHD{}_SEGMENT,".format(i_drhd_cnt), file=config)
print("\t\t.flags = DRHD{}_FLAGS,".format(i_drhd_cnt), file=config)
print("\t\t.reg_base_addr = DRHD{}_REG_BASE,".format(i_drhd_cnt), file=config)
print("\t\t.ignore = DRHD{}_IGNORE,".format(i_drhd_cnt), file=config)
print("\t\t.devices = drhd{}_dev_scope".format(i_drhd_cnt), file=config)
print("\t},", file=config)
print("};", file=config)
print("", file=config)
print("struct dmar_info plat_dmar_info = {", file=config)
print("\t.drhd_count = DRHD_COUNT,", file=config)
print("\t.drhd_units = drhd_info_array,", file=config)
print("};", file=config)
def populate_clos_mask_msr(rdt_res, cat_mask_list, config):
"""
Populate the clos bitmask and msr index for a given RDT resource
:param rdt_res: it is a string representing the RDT resource
:param cat_mask_list: cache mask list corresponding to each CLOS
:param config: it is a file pointer of board information for writing to
"""
idx = 0
for cat_mask in cat_mask_list:
print("\t{", file=config)
print("\t\t.clos_mask = {},".format(cat_mask), file=config)
print("\t},", file=config)
idx += 1
def populate_mba_delay_mask(rdt_res, mba_delay_list, config):
"""
Populate the mba delay mask and msr index for memory resource
:param rdt_res: it is a string representing the RDT resource
:param mba_delay_list: mba delay value list corresponding to each CLOS
:param config: it is a file pointer of board information for writing to
"""
idx = 0
for mba_delay_mask in mba_delay_list:
print("\t{", file=config)
print("\t\t.mba_delay = ,".format(mba_delay_mask), file=config)
print("\t},", file=config)
idx += 1
def get_rdt_enabled():
scenario_etree = lxml.etree.parse(common.SCENARIO_INFO_FILE)
enable = scenario_etree.xpath(f"//RDT_ENABLED/text()")
if enable[0] == "y":
return "true"
else:
return "false"
def get_cdp_enabled():
scenario_etree = lxml.etree.parse(common.SCENARIO_INFO_FILE)
enable = scenario_etree.xpath(f"//CDP_ENABLED/text()")
if enable[0] == "y":
return "true"
else:
return "false"
def get_common_clos_max(clos_number, capability_id):
common_clos_max = 0
if get_rdt_enabled() and not get_cdp_enabled():
common_clos_max = clos_number
if get_cdp_enabled() and capability_id != 'MBA':
common_clos_max = clos_number / 2
return common_clos_max
def gen_rdt_str(cache, config):
err_dic = {}
cat_mask_list = {}
board_etree = lxml.etree.parse(common.BOARD_INFO_FILE)
mask_length = common.get_node(f"./capability[@id='CAT']/capacity_mask_length/text()", cache)
clos_number = common.get_node(f"./capability[@id='CAT']/clos_number/text()", cache)
bitmask = (1 << int(mask_length)) - 1
cache_level = common.get_node(f"./@level", cache)
cache_id = common.get_node(f"./@id", cache)
processor_list = board_etree.xpath(f"//cache[@level = '{cache_level}' and @id = '{cache_id}']/processors/processor/text()")
capability_list = board_etree.xpath(f"//cache[@level = '{cache_level}' and @id = '{cache_id}']/capability/@id")
for capability_id in capability_list:
common_clos_max = get_common_clos_max(int(clos_number), capability_id)
if capability_id == "CAT":
if common_clos_max > MSR_IA32_L2_MASK_END - MSR_IA32_L2_MASK_BASE or\
common_clos_max > MSR_IA32_L3_MASK_END - MSR_IA32_L3_MASK_BASE:
err_dic["board config: Failed to generate board.c"] = "CLOS Mask Number is more then the reserved address region length of L2/L3 cache"
return err_dic
cdp_enable = get_cdp_enabled()
cat_mask_list = get_mask_list(cache_level, cache_id)
if len(cat_mask_list) > int(clos_number):
err_dic['board config: Failed to generate board.c'] = "CLOS Mask Number too bigger then the supported of L2/L3 cache"
return err_dic;
if cache_level == "2":
rdt_res = "l2"
elif cache_level == "3":
rdt_res = "l3"
clos_config_array = "platform_l{0}_clos_array_{1}".format(cache_level, int(cache_id, 16))
print("\t{", file=config)
print("\t\t.res.cache = {", file=config)
print("\t\t\t.bitmask = {0},".format(hex(bitmask)), file=config)
print("\t\t\t.cbm_len = {0},".format(mask_length), file=config)
print("\t\t\t.is_cdp_enabled = {0},".format(cdp_enable), file=config)
print("\t\t},", file=config)
elif capability_id == "MBA":
max_throttling_value = common.get_node(f"./capability/max_throttling_value/text()", cache)
rdt_res = "mba"
clos_config_array = "platform_mba_clos_array"
print("\t{", file=config)
print("\t\t.res.membw = {", file=config)
print("\t\t\t.mba_max = {0},".format(clos_number), file=config)
print("\t\t\t.delay_linear = {0}".format(max_throttling_value), file=config)
print("\t\t},", file=config)
print("\t\t.num_closids = {0},".format(clos_number), file=config)
print("\t\t.num_clos_config = {0},".format(len(cat_mask_list)), file=config)
print("\t\t.clos_config_array = {0},".format(clos_config_array), file=config)
cpu_mask = 0
for processor in processor_list:
core_id = common.get_node(f"//thread[apic_id = '{processor}']/cpu_id/text()", board_etree)
if core_id is None:
continue
else:
cpu_mask = cpu_mask | (1 << int(core_id))
print("\t\t.cpu_mask = {0},".format(hex(cpu_mask)), file=config)
print("\t},", file=config)
return err_dic;
def get_mask_list(cache_level, cache_id):
allocation_dir = os.path.split(common.SCENARIO_INFO_FILE)[0] + "/configs/allocation.xml"
allocation_etree = lxml.etree.parse(allocation_dir)
if cache_level == "3":
clos_list = allocation_etree.xpath(f"//clos_mask[@id = 'l3']/clos/text()")
else:
clos_list = allocation_etree.xpath(f"//clos_mask[@id = '{cache_id}']/clos/text()")
return clos_list
def gen_clos_array(cache_list, config):
err_dic = {}
res_present = [0, 0, 0]
if len(cache_list) == 0:
print("union clos_config platform_{0}_clos_array[MAX_CACHE_CLOS_NUM_ENTRIES];".format("l2"), file=config)
print("union clos_config platform_{0}_clos_array[MAX_CACHE_CLOS_NUM_ENTRIES];".format("l3"), file=config)
print("union clos_config platform_{0}_clos_array[MAX_MBA_CLOS_NUM_ENTRIES];".format("mba"), file=config)
print("struct rdt_info res_infos[RDT_INFO_NUMBER];", file=config)
else:
for idx, cache in enumerate(cache_list):
cache_level = common.get_node(f"./@level", cache)
cache_id = common.get_node(f"./@id", cache)
clos_number = common.get_node(f"./capability/clos_number/text()", cache)
if cache_level == "2":
cat_mask_list = get_mask_list(cache_level, cache_id)
array_size = len(cat_mask_list)
print("union clos_config platform_l2_clos_array_{0}[{1}] = {{".format(int(cache_id, 16), clos_number), file=config)
populate_clos_mask_msr("L2", cat_mask_list, config)
print("};\n", file=config)
res_present[RDT.L2.value] += 1
elif cache_level == "3":
cat_mask_list = get_mask_list(cache_level, cache_id)
print("union clos_config platform_l3_clos_array_{0}[{1}] = {{".format(int(cache_id, 16), clos_number), file=config)
populate_clos_mask_msr("L3", cat_mask_list, config)
print("};\n", file=config)
res_present[RDT.L3.value] += 1
elif cache_level == "MBA":
print("union clos_config platform_mba_clos_array[MAX_MBA_CLOS_NUM_ENTRIES] = {", file=config)
err_dic = populate_mba_delay_mask("mba", mba_delay_list, config)
print("};\n", file=config)
res_present[RDT.MBA.value] = 1
else:
err_dic['board config: generate board.c failed'] = "The input of {} was corrupted!".format(common.BOARD_INFO_FILE)
return err_dic
if res_present[RDT.L2.value] == 0:
print("union clos_config platform_l2_clos_array[MAX_CACHE_CLOS_NUM_ENTRIES];", file=config)
if res_present[RDT.L3.value] == 0:
print("union clos_config platform_l3_clos_array[MAX_CACHE_CLOS_NUM_ENTRIES];", file=config)
if res_present[RDT.MBA.value] == 0:
print("union clos_config platform_mba_clos_array[MAX_MBA_CLOS_NUM_ENTRIES];", file=config)
return 0
def gen_rdt_res(config):
"""
Get RDT resource (L2, L3, MBA) information
:param config: it is a file pointer of board information for writing to
"""
print("\n#ifdef CONFIG_RDT_ENABLED", file=config)
err_dic = {}
res_present = [0, 0, 0]
scenario_etree = lxml.etree.parse(common.SCENARIO_INFO_FILE)
allocation_etree = lxml.etree.parse(common.SCENARIO_INFO_FILE)
board_etree = lxml.etree.parse(common.BOARD_INFO_FILE)
cache_list = board_etree.xpath(f"//cache[capability/@id = 'CAT' or capability/@id = 'MBA']")
gen_clos_array(cache_list, config)
cache_list = board_etree.xpath(f"//cache[capability/@id = 'CAT' and @level = '2']")
if len(cache_list) > 0:
res_present[RDT.L2.value] = len(cache_list)
rdt_ins_name = "rdt_ins_l2[" + str(len(cache_list)) + "] = {"
print("struct rdt_ins {}".format(rdt_ins_name), file=config)
for idx, cache in enumerate(cache_list):
err_dic = gen_rdt_str(cache, config)
if err_dic:
return err_dic;
print("};\n", file=config)
cache_list = board_etree.xpath(f"//cache[capability/@id = 'CAT' and @level = '3']")
if len(cache_list) > 0:
res_present[RDT.L3.value] = len(cache_list)
rdt_ins_name = "rdt_ins_l3[" + str(len(cache_list)) + "] = {"
print("struct rdt_ins {}".format(rdt_ins_name), file=config)
for idx, cache in enumerate(cache_list):
err_dic = gen_rdt_str(cache, config)
if err_dic:
return err_dic;
print("};\n", file=config)
cache_list = board_etree.xpath(f"//cache[capability/@id = 'MBA']")
if len(cache_list) > 0:
res_present[RDT.L2.value] = 1
rdt_ins_name = "rdt_ins_mba[" + str(len(cache_list)) + "] = {"
print("struct rdt_ins {}".format(rdt_ins_name), file=config)
for idx, cache in enumerate(cache_list):
err_dic = gen_rdt_str(cache, config)
if err_dic:
return err_dic;
print("};\n", file=config)
print("struct rdt_type res_cap_info[RDT_NUM_RESOURCES] = {", file=config)
if res_present[RDT.L2.value] > 0:
print("\t{", file=config)
print("\t\t.res_id = RDT_RESID_L2,", file=config)
print("\t\t.msr_qos_cfg = MSR_IA32_L2_QOS_CFG,", file=config)
print("\t\t.msr_base = MSR_IA32_L2_MASK_BASE,", file=config)
print("\t\t.num_ins = {},".format(res_present[RDT.L2.value]), file=config)
print("\t\t.ins_array = rdt_ins_l2,", file=config)
print("\t},", file=config)
if res_present[RDT.L3.value] > 0:
print("\t{", file=config)
print("\t\t.res_id = RDT_RESID_L3,", file=config)
print("\t\t.msr_qos_cfg = MSR_IA32_L3_QOS_CFG,", file=config)
print("\t\t.msr_base = MSR_IA32_L3_MASK_BASE,", file=config)
print("\t\t.num_ins = {},".format(res_present[RDT.L3.value]), file=config)
print("\t\t.ins_array = rdt_ins_l3,", file=config)
print("\t},", file=config)
if res_present[RDT.MBA.value] > 0:
print("\t{", file=config)
print("\t\t.res_id = RDT_RESID_MBA,", file=config)
print("\t\t.msr_qos_cfg = MSR_IA32_MBA_QOS_CFG,", file=config)
print("\t\t.msr_base = MSR_IA32_MBA_MASK_BASE,", file=config)
print("\t\t.num_ins = {},".format(res_present[RDT.MBA.value]), file=config)
print("\t\t.ins_array = rdt_ins_mba,", file=config)
print("\t},", file=config)
print("};\n", file=config)
print("#endif\n", file=config)
return err_dic
def gen_single_data(data_lines, domain_str, config):
line_i = 0
data_statues = True
data_len = len(data_lines)
if data_len == 0:
return
for data_l in data_lines:
if line_i == 0:
if "not available" in data_l:
print(data_l.strip(), file=config)
print("static const struct acrn_{}state_data board_cpu_{}x[0];".format(domain_str, domain_str), file=config)
print("", file=config)
data_statues = False
break
else:
print("static const struct acrn_{}state_data board_cpu_{}x[{}] = {{".format(domain_str, domain_str, data_len), file=config)
print("\t{0}".format(data_l.strip()), file=config)
line_i += 1
if data_statues:
print("};\n", file=config)
def gen_px_cx(config):
"""
Get Px/Cx and store them to board.c
:param config: it is a file pointer of board information for writing to
"""
cpu_brand_lines = board_cfg_lib.get_info(
common.BOARD_INFO_FILE, "<CPU_BRAND>", "</CPU_BRAND>")
cx_lines = board_cfg_lib.get_info(common.BOARD_INFO_FILE, "<CX_INFO>", "</CX_INFO>")
px_lines = board_cfg_lib.get_info(common.BOARD_INFO_FILE, "<PX_INFO>", "</PX_INFO>")
gen_single_data(cx_lines, 'c', config)
gen_single_data(px_lines, 'p', config)
if not cpu_brand_lines:
print("\nconst struct cpu_state_table board_cpu_state_tbl;\n", file=config)
return
for brand_line in cpu_brand_lines:
cpu_brand = brand_line
print("const struct cpu_state_table board_cpu_state_tbl = {", file=config)
print("\t{0},".format(cpu_brand.strip()), file=config)
print("\t{(uint8_t)ARRAY_SIZE(board_cpu_px), board_cpu_px,", file=config)
print("\t(uint8_t)ARRAY_SIZE(board_cpu_cx), board_cpu_cx}", file=config)
print("};", file=config)
def gen_pci_hide(config):
"""Generate hide pci information for this platform"""
scenario_etree = lxml.etree.parse(common.SCENARIO_INFO_FILE)
hidden_pdev_list = [x.replace('.', ':') for x in scenario_etree.xpath(f"//HIDDEN_PDEV/text()")]
if board_cfg_lib.BOARD_NAME in list(board_cfg_lib.KNOWN_HIDDEN_PDEVS_BOARD_DB.keys()) and board_cfg_lib.KNOWN_HIDDEN_PDEVS_BOARD_DB[board_cfg_lib.BOARD_NAME] != 0:
hidden_pdev_list += board_cfg_lib.KNOWN_HIDDEN_PDEVS_BOARD_DB[board_cfg_lib.BOARD_NAME] + scenario_pdev_list
if len(hidden_pdev_list) > 0:
hidden_pdev_num = len(hidden_pdev_list)
print("const union pci_bdf plat_hidden_pdevs[MAX_HIDDEN_PDEVS_NUM] = {", file=config)
for hidden_pdev_i in range(hidden_pdev_num):
bus = hex(int(hidden_pdev_list[hidden_pdev_i].split(':')[0], 16))
dev = hex(int(hidden_pdev_list[hidden_pdev_i].split(':')[1], 16))
fun = hex(int(hidden_pdev_list[hidden_pdev_i].split(':')[2], 16))
print("\t{", file=config)
print("\t\t.bits.b = {}U,".format(bus), file=config)
print("\t\t.bits.d = {}U,".format(dev), file=config)
print("\t\t.bits.f = {}U,".format(fun), file=config)
print("\t},", file=config)
print("};", file=config)
else:
print("const union pci_bdf plat_hidden_pdevs[MAX_HIDDEN_PDEVS_NUM];", file=config)
def gen_known_caps_pci_devs(config):
"""Generate information for known capabilities of pci devices"""
known_caps_pci_devs = board_cfg_lib.get_known_caps_pci_devs()
for dev,bdf_list in known_caps_pci_devs.items():
if dev == "VMSIX":
print("", file=config)
bdf_list_len = len(bdf_list)
if bdf_list_len == 0:
print("const struct vmsix_on_msi_info vmsix_on_msi_devs[MAX_VMSIX_ON_MSI_PDEVS_NUM];", file=config)
break
for i in range(bdf_list_len):
b = bdf_list[i].split(":")[0]
d = bdf_list[i].split(":")[1].split(".")[0]
f = bdf_list[i].split(".")[1]
print("#define VMSIX_ON_MSI_DEV{}\t.bdf.bits = {{.b = 0x{}U, .d = 0x{}U, .f =0x{}U}},".format(i, b, d, f), file=config)
for i in range(bdf_list_len):
if i == 0:
print("const struct vmsix_on_msi_info vmsix_on_msi_devs[MAX_VMSIX_ON_MSI_PDEVS_NUM] = {", file=config)
print("\t{{VMSIX_ON_MSI_DEV{}}},".format(i), file=config)
if i == (bdf_list_len - 1):
print("};", file=config)
def gen_cpufreq_limits(config):
allocation_dir = os.path.split(common.SCENARIO_INFO_FILE)[0] + "/configs/allocation.xml"
allocation_etree = lxml.etree.parse(allocation_dir)
cpu_list = board_cfg_lib.get_processor_info()
max_cpu_num = len(cpu_list)
print("\nstruct acrn_cpufreq_limits cpufreq_limits[MAX_PCPU_NUM] = {", file=config)
for cpu_id in range(max_cpu_num):
limit_node = common.get_node(f"//cpufreq/CPU[@id='{cpu_id}']/limits", allocation_etree)
if limit_node != None:
limit_guaranteed_lvl = common.get_node("./limit_guaranteed_lvl/text()", limit_node)
limit_highest_lvl = common.get_node("./limit_highest_lvl/text()", limit_node)
limit_lowest_lvl = common.get_node("./limit_lowest_lvl/text()", limit_node)
limit_nominal_pstate = common.get_node("./limit_nominal_pstate/text()", limit_node)
limit_highest_pstate = common.get_node("./limit_highest_pstate/text()", limit_node)
limit_lowest_pstate = common.get_node("./limit_lowest_pstate/text()", limit_node)
print("\t{", file=config)
print(f"\t\t.guaranteed_hwp_lvl = {limit_guaranteed_lvl},", file=config)
print(f"\t\t.highest_hwp_lvl = {limit_highest_lvl},", file=config)
print(f"\t\t.lowest_hwp_lvl = {limit_lowest_lvl},", file=config)
print(f"\t\t.nominal_pstate = {limit_nominal_pstate},", file=config)
print(f"\t\t.performance_pstate = {limit_highest_pstate},", file=config)
print("\t},", file=config)
print("};", file=config)
def generate_file(config):
"""
Start to generate board.c
:param config: it is a file pointer of board information for writing to
"""
err_dic = {}
print("{0}".format(board_cfg_lib.HEADER_LICENSE), file=config)
# insert bios info into board.c
board_cfg_lib.handle_bios_info(config)
print(INCLUDE_HEADER, file=config)
# start to parse DMAR info
gen_dmar_structure(config)
# start to parse RDT resource info
err_dic = gen_rdt_res(config)
if err_dic:
return err_dic
# start to parse PX/CX info
gen_px_cx(config)
# gen hide pci info for platform
gen_pci_hide(config)
# gen known caps of pci dev info for platform
gen_known_caps_pci_devs(config)
gen_cpufreq_limits(config)
return err_dic