board_inspector/acpiparser: enable parsing RTCT v2

This patch adds support to parse RTCT v2 using the refined board XML
schema. The major changes include:

 - Add the RTCT v2 parser in the acpiparser module. The version of an RTCT
   is detected automatically to choose the right parser.
 - Extract software SRAM capabilities of caches into the board XML.
 - Move the logic that determines the software SRAM base address for the
   pre-launched VM to the static allocator of GPAs.
 - Generate software SRAM related macros into misc_cfg.h when necessary.

Tracked-On: #6020
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
Junjie Mao 2021-05-10 13:20:50 +08:00 committed by wenlingz
parent c0fef0b1fb
commit 99f15a27c2
8 changed files with 291 additions and 83 deletions

View File

@ -3,7 +3,7 @@
# SPDX-License-Identifier: BSD-3-Clause
#
import sys
import sys, os
from acpiparser.apic import APIC
from acpiparser.asf import ASF
@ -29,4 +29,11 @@ parse_asf = make_parser('ASF!')
parse_dsdt = make_parser('DSDT')
parse_dmar = make_parser('DMAR')
parse_facp = make_parser('FACP')
parse_rtct = make_parser('RTCT')
def parse_rtct(path=None):
if not path:
path = f"/sys/firmware/acpi/tables/RTCT"
if not os.path.exists(path):
path = f"/sys/firmware/acpi/tables/PTCT"
fn = getattr(sys.modules[f"acpiparser.rtct"], "RTCT")
return fn(path)

View File

@ -9,14 +9,43 @@ import copy
import lib.cdata as cdata
from acpiparser._utils import TableHeader
# Common structures
class RTCTSubtable(cdata.Struct):
_pack_ = 1
_fields_ = [
('size', ctypes.c_uint16),
('subtable_size', ctypes.c_uint16),
('format', ctypes.c_uint16),
('type', ctypes.c_uint32),
]
ACPI_RTCT_TYPE_COMPATIBILITY = 0
class RTCTSubtableCompatibility(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('rtct_version_major', ctypes.c_uint32),
('rtct_version_minor', ctypes.c_uint32),
('rtcd_version_major', ctypes.c_uint32),
('rtcd_version_minor', ctypes.c_uint32),
]
def RTCTSubtableUnknown_factory(data_len):
class RTCTSubtableUnknown(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('data', ctypes.c_uint8 * data_len),
]
return RTCTSubtableUnknown
# RTCT v1
ACPI_RTCT_V1_TYPE_RTCM_BINARY = 2
ACPI_RTCT_V1_TYPE_WRC_L3Waymasks = 3
ACPI_RTCT_V1_TYPE_GT_L3Waymasks = 4
ACPI_RTCT_V1_TYPE_SoftwareSRAM = 5
ACPI_RTCT_V1_TYPE_Memory_Hierarchy_Latency = 9
class RTCTSubtableRTCMBinary(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
@ -28,7 +57,7 @@ def RTCTSubtableWRCL3Waymasks_factory(data_len):
class RTCTSubtableWRCL3Waymasks(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('waskmask', ctypes.c_uint32 * (data_len // 4)),
('waymask', ctypes.c_uint32 * (data_len // 4)),
]
return RTCTSubtableWRCL3Waymasks
@ -36,12 +65,12 @@ def RTCTSubtableGTL3Waymasks_factory(data_len):
class RTCTSubtableGTL3Waymasks(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('waskmask', ctypes.c_uint32 * (data_len // 4)),
('waymask', ctypes.c_uint32 * (data_len // 4)),
]
return RTCTSubtableGTL3Waymasks
def RTCTSubtableSoftwareSRAM_factory(data_len):
class RTCTSubtableSoftwareSRAM(cdata.Struct):
def RTCTSubtableSoftwareSRAM_v1_factory(data_len):
class RTCTSubtableSoftwareSRAM_v1(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('cache_level', ctypes.c_uint32),
@ -50,57 +79,186 @@ def RTCTSubtableSoftwareSRAM_factory(data_len):
('size', ctypes.c_uint32),
('apic_id_tbl', ctypes.c_uint32 * ((data_len - 20) // 4)),
]
return RTCTSubtableSoftwareSRAM
return RTCTSubtableSoftwareSRAM_v1
def RTCTSubtableMemoryHierarchyLatency_factory(data_len):
class RTCTSubtableMemoryHierarchyLatency(cdata.Struct):
def RTCTSubtableMemoryHierarchyLatency_v1_factory(data_len):
class RTCTSubtableMemoryHierarchyLatency_v1(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('hierarchy', ctypes.c_uint32),
('clock_cycles', ctypes.c_uint32),
('apic_id_tbl', ctypes.c_uint32 * ((data_len - 8) // 4)),
]
return RTCTSubtableMemoryHierarchyLatency
return RTCTSubtableMemoryHierarchyLatency_v1
def RTCTSubtableUnknown_factory(data_len):
class RTCTSubtableUnknown(cdata.Struct):
# RTCT v2
ACPI_RTCT_V2_TYPE_RTCD_Limits = 1
ACPI_RTCT_V2_TYPE_CRL_Binary = 2
ACPI_RTCT_V2_TYPE_IA_WayMasks = 3
ACPI_RTCT_V2_TYPE_WRC_WayMasks = 4
ACPI_RTCT_V2_TYPE_GT_WayMasks = 5
ACPI_RTCT_V2_TYPE_SSRAM_WayMask = 6
ACPI_RTCT_V2_TYPE_SoftwareSRAM = 7
ACPI_RTCT_V2_TYPE_MemoryHierarchyLatency = 8
ACPI_RTCT_V2_TYPE_ErrorLogAddress = 9
class RTCTSubtableRTCDLimits(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('total_ia_l2_clos', ctypes.c_uint32),
('total_ia_l3_clos', ctypes.c_uint32),
('total_l2_instances', ctypes.c_uint32),
('total_l3_instances', ctypes.c_uint32),
('total_gt_clos', ctypes.c_uint32),
('total_wrc_clos', ctypes.c_uint32),
('max_tcc_streams', ctypes.c_uint32),
('max_tcc_registers', ctypes.c_uint32),
]
def RTCTSubtableIAWayMasks_factory(data_len):
class RTCTSubtableIAWayMasks(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('data', ctypes.c_uint8 * data_len),
('level', ctypes.c_uint32),
('cache_id', ctypes.c_uint32),
('waymask', ctypes.c_uint32 * ((data_len - 8) // 4)),
]
return RTCTSubtableUnknown
return RTCTSubtableIAWayMasks
ACPI_RTCT_TYPE_RTCM_BINARY = 2
ACPI_RTCT_TYPE_WRC_L3Waymasks = 3
ACPI_RTCT_TYPE_GT_L3Waymasks = 4
ACPI_RTCT_TYPE_SoftwareSRAM = 5
ACPI_RTCT_TYPE_Memory_Hierarchy_Latency = 9
class RTCTSubtableWRCWayMasks(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('level', ctypes.c_uint32),
('cache_id', ctypes.c_uint32),
('waymask', ctypes.c_uint32),
]
def rtct_subtable_list(addr, length):
def RTCTSubtableGTWayMasks_factory(data_len):
class RTCTSubtableGTWayMasks(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('level', ctypes.c_uint32),
('cache_id', ctypes.c_uint32),
('waymask', ctypes.c_uint32 * ((data_len - 8) // 4)),
]
return RTCTSubtableGTWayMasks
class RTCTSubtableSSRAMWayMask(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('level', ctypes.c_uint32),
('cache_id', ctypes.c_uint32),
('waymask', ctypes.c_uint32),
]
def RTCTSubtableSoftwareSRAM_v2_factory(data_len):
class RTCTSubtableSoftwareSRAM_v2(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('level', ctypes.c_uint32),
('cache_id', ctypes.c_uint32),
('base', ctypes.c_uint64),
('size', ctypes.c_uint32),
('shared', ctypes.c_uint32),
]
return RTCTSubtableSoftwareSRAM_v2
def RTCTSubtableMemoryHierarchyLatency_v2_factory(data_len):
class RTCTSubtableMemoryHierarchyLatency_v2(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('hierarchy', ctypes.c_uint32),
('clock_cycles', ctypes.c_uint32),
('cache_id', ctypes.c_uint32 * ((data_len - 8) // 4)),
]
return RTCTSubtableMemoryHierarchyLatency_v2
class RTCTSubtableErrorLogAddress(cdata.Struct):
_pack_ = 1
_fields_ = copy.copy(RTCTSubtable._fields_) + [
('address', ctypes.c_uint64),
('size', ctypes.c_uint32),
]
# Parsers
def rtct_version(addr, length):
end = addr + length
while addr < end:
subtable = RTCTSubtable.from_address(addr)
if subtable.type == ACPI_RTCT_TYPE_COMPATIBILITY:
subtable = RTCTSubtableCompatibility.from_address(addr)
return subtable.rtct_version_major
addr += subtable.subtable_size
# RTCT v1 does not have a compatibility entry
return 1
def rtct_v1_subtable_list(addr, length):
end = addr + length
field_list = list()
subtable_num = 0
while addr < end:
subtable_num += 1
subtable = RTCTSubtable.from_address(addr)
data_len = subtable.size - ctypes.sizeof(RTCTSubtable)
if subtable.type == ACPI_RTCT_TYPE_RTCM_BINARY:
data_len = subtable.subtable_size - ctypes.sizeof(RTCTSubtable)
if subtable.type == ACPI_RTCT_V1_TYPE_RTCM_BINARY:
cls = RTCTSubtableRTCMBinary
elif subtable.type == ACPI_RTCT_TYPE_WRC_L3Waymasks:
elif subtable.type == ACPI_RTCT_V1_TYPE_WRC_L3Waymasks:
cls = RTCTSubtableWRCL3Waymasks_factory(data_len)
elif subtable.type == ACPI_RTCT_TYPE_GT_L3Waymasks:
elif subtable.type == ACPI_RTCT_V1_TYPE_GT_L3Waymasks:
cls = RTCTSubtableGTL3Waymasks_factory(data_len)
elif subtable.type == ACPI_RTCT_TYPE_SoftwareSRAM:
cls = RTCTSubtableSoftwareSRAM_factory(data_len)
elif subtable.type == ACPI_RTCT_TYPE_Memory_Hierarchy_Latency:
cls = RTCTSubtableMemoryHierarchyLatency_factory(data_len)
elif subtable.type == ACPI_RTCT_V1_TYPE_SoftwareSRAM:
cls = RTCTSubtableSoftwareSRAM_v1_factory(data_len)
elif subtable.type == ACPI_RTCT_V1_TYPE_Memory_Hierarchy_Latency:
cls = RTCTSubtableMemoryHierarchyLatency_v1_factory(data_len)
else:
cls = RTCTSubtableUnknown_factory(data_len)
addr += subtable.size
addr += subtable.subtable_size
field_list.append( ('subtable{}'.format(subtable_num), cls) )
return field_list
def rtct_factory(field_list):
def rtct_v2_subtable_list(addr, length):
end = addr + length
field_list = list()
subtable_num = 0
while addr < end:
subtable_num += 1
subtable = RTCTSubtable.from_address(addr)
data_len = subtable.subtable_size - ctypes.sizeof(RTCTSubtable)
if subtable.type == ACPI_RTCT_V2_TYPE_RTCD_Limits:
cls = RTCTSubtableRTCDLimits
elif subtable.type == ACPI_RTCT_V2_TYPE_CRL_Binary:
cls = RTCTSubtableRTCMBinary
elif subtable.type == ACPI_RTCT_V2_TYPE_IA_WayMasks:
cls = RTCTSubtableIAWayMasks_factory(data_len)
elif subtable.type == ACPI_RTCT_V2_TYPE_WRC_WayMasks:
cls = RTCTSubtableWRCWayMasks
elif subtable.type == ACPI_RTCT_V2_TYPE_GT_WayMasks:
cls = RTCTSubtableGTWayMasks_factory(data_len)
elif subtable.type == ACPI_RTCT_V2_TYPE_SSRAM_WayMask:
cls = RTCTSubtableSSRAMWayMask
elif subtable.type == ACPI_RTCT_V2_TYPE_SoftwareSRAM:
cls = RTCTSubtableSoftwareSRAM_v2_factory(data_len)
elif subtable.type == ACPI_RTCT_V2_TYPE_MemoryHierarchyLatency:
cls = RTCTSubtableMemoryHierarchyLatency_v2_factory(data_len)
elif subtable.type == ACPI_RTCT_V2_TYPE_ErrorLogAddress:
cls = RTCTSubtableErrorLogAddress
else:
cls = RTCTSubtableUnknown_factory(data_len)
addr += subtable.subtable_size
field_list.append( ('subtable{}'.format(subtable_num), cls) )
return field_list
def rtct_version_and_subtable_list(addr, length):
version = rtct_version(addr, length)
if version == 1:
return (version, rtct_v1_subtable_list(addr, length))
else:
return (version, rtct_v2_subtable_list(addr, length))
def rtct_factory(field_list, version):
class subtables(cdata.Struct):
_pack_ = 1
@ -117,14 +275,18 @@ def rtct_factory(field_list):
('entries', subtables),
]
@property
def version(self):
return version
return RTCT
def RTCT(val):
"""Create class based on decode of an RTCT table from filename."""
base_length = ctypes.sizeof(rtct_factory(list()))
base_length = ctypes.sizeof(rtct_factory(list(), 0))
data = open(val, mode='rb').read()
buf = ctypes.create_string_buffer(data, len(data))
addr = ctypes.addressof(buf)
hdr = TableHeader.from_address(addr)
field_list = rtct_subtable_list(addr + base_length, hdr.length - base_length)
return rtct_factory(field_list).from_buffer_copy(data)
version, field_list = rtct_version_and_subtable_list(addr + base_length, hdr.length - base_length)
return rtct_factory(field_list, version).from_buffer_copy(data)

View File

@ -8,6 +8,8 @@ import lxml.etree
from extractors.helpers import add_child, get_node
from cpuparser import parse_cpuid
from acpiparser import parse_rtct
import acpiparser.rtct
def extract_topology(root_node, caches_node):
threads = root_node.xpath("//processors//*[cpu_id]")
@ -48,11 +50,11 @@ def extract_topology(root_node, caches_node):
else:
leaf_10 = None
if leaf_10 is not None:
cap = add_child(n, "capability", None, kind="cat")
cap = add_child(n, "capability", None, id="CAT")
add_child(cap, "capacity_mask_length", str(leaf_10.capacity_mask_length))
add_child(cap, "clos_number", str(leaf_10.clos_number))
if leaf_10.code_and_data_prioritization == 1:
add_child(n, "capability", None, kind="cdp")
add_child(n, "capability", None, id="CDP")
add_child(get_node(n, "processors"), "processor", get_node(thread, "apic_id/text()"))
@ -65,7 +67,36 @@ def extract_topology(root_node, caches_node):
return (level, id, type)
caches_node[:] = sorted(caches_node, key=getkey)
def extract_tcc_capabilities(caches_node):
try:
rtct = parse_rtct()
if rtct.version == 1:
for entry in rtct.entries:
if entry.type == acpiparser.rtct.ACPI_RTCT_V1_TYPE_SoftwareSRAM:
cache_node = get_node(caches_node, f"cache[@level='{entry.cache_level}' and processors/processor='{hex(entry.apic_id_tbl[0])}']")
if cache_node is None:
logging.warning(f"Cannot find the level {entry.cache_level} cache of physical processor with apic ID {entry.apic_id_tbl[0]}")
continue
cap = add_child(cache_node, "capability", None, id="Software SRAM")
add_child(cap, "start", "0x{:08x}".format(entry.base))
add_child(cap, "end", "0x{:08x}".format(entry.base + entry.size - 1))
add_child(cap, "size", str(entry.size))
elif rtct.version == 2:
for entry in rtct.entries:
if entry.type == acpiparser.rtct.ACPI_RTCT_V2_TYPE_SoftwareSRAM:
cache_node = get_node(caches_node, f"cache[@level='{entry.level}' and @id='{hex(entry.cache_id)}']")
if cache_node is None:
logging.warning(f"Cannot find the level {entry.level} cache with cache ID {entry.cache_id}")
continue
cap = add_child(cache_node, "capability", None, id="Software SRAM")
add_child(cap, "start", "0x{:08x}".format(entry.base))
add_child(cap, "end", "0x{:08x}".format(entry.base + entry.size - 1))
add_child(cap, "size", str(entry.size))
except FileNotFoundError:
pass
def extract(board_etree):
root_node = board_etree.getroot()
caches_node = get_node(board_etree, "//caches")
extract_topology(root_node, caches_node)
extract_tcc_capabilities(caches_node)

View File

@ -14,7 +14,6 @@ import acpi
import clos
import misc
import parser_lib
import rtct
OUTPUT = "./out/"
PY_CACHE = "__pycache__"
@ -135,9 +134,6 @@ if __name__ == '__main__':
# Generate misc info
misc.generate_info(BOARD_INFO)
# Generate pseudo RAM info
rtct.generate_info(BOARD_INFO)
with open(BOARD_INFO, 'a+') as f:
print("</acrn-config>", file=f)

View File

@ -1,43 +0,0 @@
# Copyright (C) 2021 Intel Corporation. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
import os
from acpiparser import parse_rtct
import acpiparser.rtct
import parser_lib
def dump_ssram(config):
print("\t<RTCT>", file=config)
rtct = None
if os.path.exists("/sys/firmware/acpi/tables/PTCT"):
rtct = parse_rtct(path="/sys/firmware/acpi/tables/PTCT")
elif os.path.exists("/sys/firmware/acpi/tables/RTCT"):
rtct = parse_rtct(path="/sys/firmware/acpi/tables/RTCT")
if rtct:
for entry in rtct.entries:
if entry.type == acpiparser.rtct.ACPI_RTCT_TYPE_SoftwareSRAM:
print("\t\t<SoftwareSRAM>", file=config)
print("\t\t\t<cache_level>{}</cache_level>".format(entry.cache_level), file=config)
print("\t\t\t<base>{}</base>".format(hex(entry.base)), file=config)
print("\t\t\t<ways>{}</ways>".format(hex(entry.ways)), file=config)
print("\t\t\t<size>{}</size>".format(hex(entry.size)), file=config)
for apic_id in entry.apic_id_tbl:
print("\t\t\t<apic_id>{}</apic_id>".format(hex(apic_id)), file=config)
print("\t\t</SoftwareSRAM>", file=config)
else:
parser_lib.print_yel("No PTCT or RTCT found. The platform may not support pseudo RAM.")
print("\t</RTCT>", file=config)
print("", file=config)
def generate_info(board_info):
"""Get system pseudo RAM information
:param board_info: this is the file which stores the hardware board information
"""
with open(board_info, 'a+') as config:
dump_ssram(config)

View File

@ -36,4 +36,20 @@ or little cores are assigned, but not both.</xs:documentation>
</xs:annotation>
</xs:assert>
<xs:assert test="hv//PSRAM_ENABLED = 'n' or empty(vm[vm_type='PRE_RT_VM']) or
every $cap in caches/cache[@level=3]/capability[@id='Software SRAM'] satisfies
(compare($cap/end, '0x80000000') &lt; 0 or compare($cap/start, '0xf8000000') &gt;= 0)">
<xs:annotation acrn:severity="warning">
<xs:documentation>The physical software SRAM region shall not overlap with pre-defined regions in guest.
When a pre-launched RT VM is enabled, the physical software SRAM is allocated to it at the same guest physical
address. Thus it is assumed that the software SRAM region does not overlap with any pre-defined region in the
pre-launched VM, such as the guest PCI hole which resides at 2G - 3.5G.
This error cannot be fixed by tweaking the configurations. Report to _GitHub:
https://github.com/projectacrn/acrn-hypervisor/issues if you meet this.</xs:documentation>
</xs:annotation>
</xs:assert>
</xs:schema>

View File

@ -0,0 +1,31 @@
#!/usr/bin/env python3
#
# Copyright (C) 2021 Intel Corporation. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
import sys, os
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'library'))
import common
def allocate_ssram_region(board_etree, scenario_etree, allocation_etree):
# Guest physical address of the SW SRAM allocated to a pre-launched VM
enabled = common.get_node("//PSRAM_ENABLED/text()", scenario_etree)
if enabled == "y":
pre_rt_vms = common.get_node("//vm[vm_type ='PRE_RT_VM']", scenario_etree)
if pre_rt_vms is not None:
vm_id = pre_rt_vms.get("id")
l3_sw_sram = board_etree.xpath("//cache[@level='3']/capability[@id='Software SRAM']")
if l3_sw_sram:
start = min(map(lambda x: int(x.find("start").text, 16), l3_sw_sram))
end = max(map(lambda x: int(x.find("end").text, 16), l3_sw_sram))
allocation_vm_node = common.get_node(f"/acrn-config/vm[@id = '{vm_id}']", allocation_etree)
if allocation_vm_node is None:
allocation_vm_node = common.append_node("/acrn-config/vm", None, allocation_etree, id = vm_id)
common.append_node("./ssram/start_gpa", hex(start), allocation_vm_node)
common.append_node("./ssram/end_gpa", hex(end), allocation_vm_node)
def fn(board_etree, scenario_etree, allocation_etree):
allocate_ssram_region(board_etree, scenario_etree, allocation_etree)

View File

@ -22,6 +22,8 @@
<xsl:apply-templates select="config-data/acrn-config" />
<xsl:apply-templates select="allocation-data//ssram" />
<xsl:value-of select="acrn:include-guard-end('MISC_CFG_H')" />
</xsl:template>
@ -40,6 +42,12 @@
<xsl:call-template name="vm_pt_intx_num" />
</xsl:template>
<xsl:template match="allocation-data//ssram">
<xsl:value-of select="acrn:define('PRE_RTVM_SW_SRAM_ENABLED', 1, '')" />
<xsl:value-of select="acrn:define('PRE_RTVM_SW_SRAM_BASE_GPA', start_gpa, 'UL')" />
<xsl:value-of select="acrn:define('PRE_RTVM_SW_SRAM_END_GPA', end_gpa, 'UL')" />
</xsl:template>
<xsl:template name="sos_rootfs">
<xsl:value-of select="acrn:define('SOS_ROOTFS', concat($quot, 'root=', vm/board_private/rootfs[text()], ' ', $quot), '')" />
</xsl:template>