acrn-hypervisor/misc/config_tools/acpi_gen/asl_gen.py
Zhangwei6 056f4abcfb config_tools: fix pre_launch VM ACPI table
Current acpi table adopts the DWordAddressSpace to
present the PCI hole above 4G. It is not correct,
because DWordAddressSpace is used to present the
mem space below 4G.
Follow the ACPI spec, modify to QWordAddressSpace here.

Refer: 19.6.22. CreateQWordField (Create 64-Bit Buffer Field)

Tracked-On: #8552
Signed-off-by: Zhangwei6 <wei6.zhang@intel.com>
Reviewed-by: Junjie Mao <junjie.mao@intel.com>
2024-02-04 15:36:02 +08:00

962 lines
40 KiB
Python

# Copyright (C) 2019-2022 Intel Corporation.
# SPDX-License-Identifier: BSD-3-Clause
"""the tool to generate ASL code of ACPI tables for Pre-launched VMs.
"""
import sys, os, re, argparse, shutil, ctypes
from acpi_const import *
import board_cfg_lib, acrn_config_utilities
import collections
import lxml.etree
from acrn_config_utilities import get_node
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'board_inspector'))
from acpiparser._utils import TableHeader
from acpiparser import rtct
from acpiparser import rdt
from acpiparser.dsdt import parse_tree
from acpiparser.aml import builder
from acpiparser.aml.context import Context
from acpiparser.aml.visitors import GenerateBinaryVisitor, PrintLayoutVisitor
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_rtct_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_rtct_addr, line):
if 'PTCT' in passthru_devices or 'RTCT' in passthru_devices:
lines.append(re.sub(p_rtct_addr, 'ACPI Table Address 4 : {0:016X}'.format(ACPI_RTCT_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 encode_eisa_id(s):
chars = list(map(lambda x: (ord(x) - 0x40) & 0x1F, s[0:3]))
digits = list(map(lambda x: int(x, 16), s[3:7]))
encoded = [
(chars[0] << 2) | (chars[1] >> 3), # Bit 6:2 is char[0]; Bit 1:0 is the higher 2 bits of char[1].
((chars[1] & 0x7) << 5) | (chars[2]), # Bit 7:5 is the lower 3 bits of char[1]; Bit 4:0 is char[2].
(digits[0] << 4) | (digits[1]), # Bit 7:4 is digits[0]; Bit 3:0 is digits[1]
(digits[2] << 4) | (digits[3]), # Bit 7:4 is digits[2]; Bit 3:0 is digits[2]
]
return int.from_bytes(bytes(encoded), sys.byteorder)
def create_object(cls, **kwargs):
length = ctypes.sizeof(cls)
data = bytearray(length)
obj = cls.from_buffer(data)
for key, value in kwargs.items():
setattr(obj, key, value)
return obj
def gen_root_pci_bus(path, prt_packages):
resources = []
# Bus number
word_address_space_cls = rdt.LargeResourceItemWordAddressSpace_factory()
res = create_object(
word_address_space_cls,
type = 1, # Large type
name = rdt.LARGE_RESOURCE_ITEM_WORD_ADDRESS_SPACE,
length = ctypes.sizeof(word_address_space_cls) - 3,
_TYP = 2, # Bus number range
_DEC = 0, # Positive decoding
_MIF = 1, # Minimum address fixed
_MAF = 1, # Maximum address fixed
flags = 0,
_MAX = 0xff,
_LEN = 0x100
)
resources.append(res)
# The PCI hole below 4G
dword_address_space_cls = rdt.LargeResourceItemDWordAddressSpace_factory()
res = create_object(
dword_address_space_cls,
type = 1, # Large type
name = rdt.LARGE_RESOURCE_ITEM_ADDRESS_SPACE_RESOURCE,
length = ctypes.sizeof(dword_address_space_cls) - 3,
_TYP = 0, # Memory range
_DEC = 0, # Positive decoding
_MIF = 1, # Minimum address fixed
_MAF = 1, # Maximum address fixed
flags = 1, # read-write, non-cachable, TypeStatic
_MIN = 0x80000000,
_MAX = 0xdfffffff,
_LEN = 0x60000000
)
resources.append(res)
# The PCI hole above 4G
qword_address_space_cls = rdt.LargeResourceItemQWordAddressSpace_factory()
res = create_object(
qword_address_space_cls,
type = 1, # Large type
name = rdt.LARGE_RESOURCE_ITEM_QWORD_ADDRESS_SPACE,
length = ctypes.sizeof(qword_address_space_cls) - 3,
_TYP = 0, # Memory range
_DEC = 0, # Positive decoding
_MIF = 1, # Minimum address fixed
_MAF = 1, # Maximum address fixed
flags = 1, # read-write, non-cachable, TypeStatic
_MIN = 0x4000000000,
_MAX = 0x7fffffffff,
_LEN = 0x4000000000
)
resources.append(res)
# The PCI hole for I/O ports
res = create_object(
dword_address_space_cls,
type = 1, # Large type
name = rdt.LARGE_RESOURCE_ITEM_ADDRESS_SPACE_RESOURCE,
length = ctypes.sizeof(dword_address_space_cls) - 3,
_TYP = 1, # I/O range
_DEC = 0, # Positive decoding
_MIF = 1, # Minimum address fixed
_MAF = 1, # Maximum address fixed
flags = 3, # Entire range, TypeStatic
_MIN = 0x0,
_MAX = 0xffff,
_LEN = 0x10000
)
resources.append(res)
# End tag
resources.append(bytes([0x79, 0]))
resource_buf = bytearray().join(map(bytearray, resources))
checksum = (256 - (sum(resource_buf) % 256)) % 256
resource_buf[-1] = checksum
# Device object for the root PCI bus
tree = builder.DefDevice(
builder.PkgLength(),
path,
builder.TermList(
builder.DefName(
"_HID",
builder.DWordConst(encode_eisa_id("PNP0A08"))),
builder.DefName(
"_CID",
builder.DWordConst(encode_eisa_id("PNP0A03"))),
builder.DefName(
"_BBN",
builder.ZeroOp()),
builder.DefName(
"_UID",
builder.ZeroOp()),
builder.DefName(
"_CRS",
builder.DefBuffer(
builder.PkgLength(),
builder.WordConst(len(resource_buf)),
builder.ByteList(resource_buf))),
builder.DefName(
"_PRT",
builder.DefPackage(
builder.PkgLength(),
builder.ByteData(len(prt_packages)),
builder.PackageElementList(*prt_packages)))))
return tree
def pnp_uart(path, uid, ddn, port, irq):
resources = []
res = create_object(
rdt.SmallResourceItemIOPort,
type = 0,
name = rdt.SMALL_RESOURCE_ITEM_IO_PORT,
length = ctypes.sizeof(rdt.SmallResourceItemIOPort) - 1,
_DEC = 1,
_MIN = port,
_MAX = port,
_ALN = 1,
_LEN = 8
)
resources.append(res)
cls = rdt.SmallResourceItemIRQ_factory()
res = create_object(
cls,
type = 0,
name = rdt.SMALL_RESOURCE_ITEM_IRQ_FORMAT,
length = ctypes.sizeof(cls) - 1,
_INT = 1 << irq
)
resources.append(res)
resources.append(bytes([0x79, 0]))
resource_buf = bytearray().join(map(bytearray, resources))
checksum = (256 - (sum(resource_buf) % 256)) % 256
resource_buf[-1] = checksum
uart = builder.DefDevice(
builder.PkgLength(),
path,
builder.TermList(
builder.DefName(
"_HID",
builder.DWordConst(encode_eisa_id("PNP0501"))),
builder.DefName(
"_UID",
builder.build_value(uid)),
builder.DefName(
"_DDN",
builder.String(ddn)),
builder.DefName(
"_CRS",
builder.DefBuffer(
builder.PkgLength(),
builder.WordConst(len(resource_buf)),
builder.ByteList(resource_buf)))))
return uart
def pnp_rtc(path):
resources = []
res = create_object(
rdt.SmallResourceItemIOPort,
type = 0,
name = rdt.SMALL_RESOURCE_ITEM_IO_PORT,
length = ctypes.sizeof(rdt.SmallResourceItemIOPort) - 1,
_DEC = 1,
_MIN = 0x70,
_MAX = 0x70,
_ALN = 1,
_LEN = 8
)
resources.append(res)
cls = rdt.SmallResourceItemIRQ_factory()
res = create_object(
cls,
type = 0,
name = rdt.SMALL_RESOURCE_ITEM_IRQ_FORMAT,
length = ctypes.sizeof(cls) - 1,
_INT = 1 << 8
)
resources.append(res)
resources.append(bytes([0x79, 0]))
resource_buf = bytearray().join(map(bytearray, resources))
checksum = (256 - (sum(resource_buf) % 256)) % 256
resource_buf[-1] = checksum
rtc = builder.DefDevice(
builder.PkgLength(),
path,
builder.TermList(
builder.DefName(
"_HID",
builder.DWordConst(encode_eisa_id("PNP0B00"))),
builder.DefName(
"_CRS",
builder.DefBuffer(
builder.PkgLength(),
builder.WordConst(len(resource_buf)),
builder.ByteList(resource_buf)))))
return rtc
def collect_dependent_devices(board_etree, device_node):
types_in_scope = ["uses", "is used by", "consumes resources from"]
result = set()
queue = [device_node]
while queue:
device = queue.pop()
if device not in result:
result.add(device)
for node in device.findall("dependency"):
if node.get("type") in types_in_scope:
peer_device = get_node(f"//device[acpi_object='{node.text}']", board_etree)
if peer_device is not None:
queue.append(peer_device)
result.discard(device_node)
return result
def add_or_replace_object(device_object, new_tree):
name = new_tree.NameString.value
objects_tree = device_object.TermList
for idx, object_tree in enumerate(objects_tree.children):
if object_tree.NameString.value == name:
objects_tree.children[idx] = new_tree
return
objects_tree.append_child(new_tree)
class ObjectCollector:
def __init__(self):
self.__objects = collections.defaultdict(lambda: [])
@staticmethod
def __discard_external_objects(tree):
objects_tree = tree.TermList
objects_tree.children = list(filter(lambda x: x.label != "DefExternal", objects_tree.children))
return tree
def add_device_object(self, device_object):
device_path = device_object.NameString.value
scope = Context.parent(device_path)
device_name = device_path[-4:]
device_object.NameString.value = device_name
self.__objects[scope].append(self.__discard_external_objects(device_object))
def add_object(self, scope, obj):
self.__objects[scope].append(obj)
def __get_scope_contents(self, termlist, path):
scopes = [i for i in path.lstrip("\\").split(".") if i]
ret = termlist
for scope in scopes:
for term in ret:
if term.label in ["DefScope", "DefDevice"] and term.NameString.value == scope:
ret = term.TermList.children
break
else:
tree = builder.DefScope(builder.PkgLength(), scope, builder.TermList())
ret.append(tree)
ret = tree.TermList.children
return ret
def get_term_list(self):
acc = []
for scope, objects in sorted(self.__objects.items(), key=lambda p:p[0]):
if scope.startswith("\\"):
self.__get_scope_contents(acc, scope).extend(objects)
else:
print(f"Relative scope is unexpected: {scope}. The objects will be added to the root scope.")
acc.extend(objects)
return acc
def gen_dsdt(board_etree, scenario_etree, allocation_etree, vm_id, dest_path):
interrupt_pin_ids = {
"INTA#": 0,
"INTB#": 1,
"INTC#": 2,
"INTD#": 3,
}
header = builder.DefBlockHeader(
int.from_bytes(bytearray("DSDT", "ascii"), sys.byteorder), # Signature
0x0badbeef, # Length, will calculate later
3, # Revision
0, # Checksum, will calculate later
int.from_bytes(bytearray("ACRN ", "ascii"), sys.byteorder), # OEM ID
int.from_bytes(bytearray("ACRNDSDT", "ascii"), sys.byteorder), # OEM Table ID
1, # OEM Revision
int.from_bytes(bytearray("INTL", "ascii"), sys.byteorder), # Compiler ID
0x20190703, # Compiler Version
)
objects = ObjectCollector()
prt_packages = []
pci_dev_regex = re.compile(r"([0-9a-f]{2}):([0-1][0-9a-f]{1}).([0-7]) .*")
for pci_dev in scenario_etree.xpath(f"//vm[@id='{vm_id}']/pci_devs/pci_dev/text()"):
m = pci_dev_regex.match(pci_dev)
if m:
device_number = int(m.group(2), 16)
function_number = int(m.group(3))
bus_number = int(m.group(1), 16)
bdf = f"{bus_number:02x}:{device_number:02x}.{function_number}"
address = hex((device_number << 16) | (function_number))
device_node = get_node(f"//bus[@address='{hex(bus_number)}']/device[@address='{address}']", board_etree)
alloc_node = get_node(f"/acrn-config/vm[@id='{vm_id}']/device[@name='PTDEV_{bdf}']", allocation_etree)
if device_node is not None and alloc_node is not None:
assert int(alloc_node.find("bus").text, 16) == 0, "Virtual PCI devices must be on bus 0."
vdev = int(alloc_node.find("dev").text, 16)
vfunc = int(alloc_node.find("func").text, 16)
# The AML object template, with _ADR replaced to vBDF
template = device_node.find("aml_template")
if template is not None:
tree = parse_tree("DefDevice", bytes.fromhex(template.text))
vaddr = (vdev << 16) | vfunc
add_or_replace_object(tree,
builder.DefName("_ADR", builder.build_value(vaddr)))
objects.add_device_object(tree)
# The _PRT remapping package, if necessary
intr_pin_node = get_node("resource[@type='interrupt_pin']", device_node)
virq_node = get_node("pt_intx", alloc_node)
if intr_pin_node is not None and virq_node is not None:
pin_id = interrupt_pin_ids[intr_pin_node.get("pin")]
vaddr = (vdev << 16) | 0xffff
pirq = int(intr_pin_node.get("source", -1))
virq_mapping = dict(eval(f"[{virq_node.text.replace(' ','').replace(')(', '), (')}]"))
if pirq in virq_mapping.keys():
virq = virq_mapping[pirq]
prt_packages.append(
builder.DefPackage(
builder.PkgLength(),
builder.ByteData(4),
builder.PackageElementList(
builder.build_value(vaddr),
builder.build_value(pin_id),
builder.build_value(0),
builder.build_value(virq))))
for peer_device_node in collect_dependent_devices(board_etree, device_node):
template = peer_device_node.find("aml_template")
if template is not None:
tree = parse_tree("DefDevice", bytes.fromhex(template.text))
objects.add_device_object(tree)
root_pci_bus = board_etree.xpath("//bus[@type='pci' and @address='0x0']")
if root_pci_bus:
acpi_object = root_pci_bus[0].find("acpi_object")
if acpi_object is not None:
path = acpi_object.text
objects.add_device_object(gen_root_pci_bus(path, prt_packages))
# If TPM is assigned to the VM, copy the TPM2 device object to vACPI as well.
#
# FIXME: Today the TPM2 MMIO registers are always located at 0xFED40000 with length 0x5000. The same address is used
# as the guest physical address of the passed through TPM2. Thus, it works for now to reuse the host TPM2 device
# object without updating the addresses of operation regions or resource descriptors. It is, however, necessary to
# introduce a pass to translate such address to arbitrary guest physical ones in the future.
has_tpm2 = get_node(f"//vm[@id='{vm_id}']//TPM2/text()", scenario_etree)
if has_tpm2 == "y":
# TPM2 devices should have "MSFT0101" as hardware id or compatible ID
template = get_node("//device[@id='MSFT0101']/aml_template", board_etree)
if template is None:
template = get_node("//device[compatible_id='MSFT0101']/aml_template", board_etree)
if template is not None:
tree = parse_tree("DefDevice", bytes.fromhex(template.text))
objects.add_device_object(tree)
s5_object = builder.DefName(
"_S5_",
builder.DefPackage(
builder.PkgLength(),
2,
builder.PackageElementList(
builder.build_value(5),
builder.build_value(0))))
objects.add_object("\\", s5_object)
rtvm = bool(scenario_etree.xpath(f"//vm[@id='{vm_id}']//lapic_passthrough[text()='y']"))
# RTVM cannot set IRQ because no INTR is sent with LAPIC PT
if rtvm is False:
objects.add_object("\\_SB_", pnp_uart("UAR0", 0, "COM1", 0x3f8, 4))
objects.add_object("\\_SB_", pnp_uart("UAR1", 1, "COM2", 0x2f8, 3))
objects.add_object("\\_SB_", pnp_rtc("RTC0"))
amlcode = builder.AMLCode(header, *objects.get_term_list())
with open(dest_path, "wb") as dest:
visitor = GenerateBinaryVisitor()
binary = bytearray(visitor.generate(amlcode))
# Overwrite length
binary[4:8] = len(binary).to_bytes(4, sys.byteorder)
# Overwrite checksum
checksum = (256 - (sum(binary) % 256)) % 256
binary[9] = checksum
dest.write(binary)
def gen_rtct(board_etree, scenario_etree, allocation_etree, vm_id, dest_path):
def cpu_id_to_lapic_id(cpu_id):
return get_node(f"//thread[cpu_id = '{cpu_id}']/apic_id/text()", board_etree)
vm_node = get_node(f"//vm[@id='{vm_id}']", scenario_etree)
if vm_node is None:
return False
vcpus = ",".join(map(cpu_id_to_lapic_id, vm_node.xpath("cpu_affinity//pcpu_id/text()")))
rtct_entries = []
# ACPI table header
common_header = create_object(
TableHeader,
signature = b'RTCT',
revision = 1,
oemid = b'ACRN ',
oemtableid = b'ACRNRTCT',
oemrevision = 5,
creatorid = b'INTL',
creatorrevision = 0x100000d
)
rtct_entries.append(common_header)
# Compatibility entry
compat_entry = create_object(
rtct.RTCTSubtableCompatibility,
subtable_size = ctypes.sizeof(rtct.RTCTSubtableCompatibility),
format_or_version = 1,
type = rtct.ACPI_RTCT_TYPE_COMPATIBILITY,
rtct_version_major = 2,
rtct_version_minor = 0,
rtcd_version_major = 0,
rtcd_version_minor = 0
)
rtct_entries.append(compat_entry)
# SSRAM entries
# Look for the cache blocks that are visible to this VM and have software SRAM in it. Those software SRAM will be
# exposed to the VM in RTCT.
for cache in board_etree.xpath(f"//caches/cache[count(processors/processor[contains('{vcpus}', .)]) and capability[@id = 'Software SRAM']]"):
ssram_cap = get_node("capability[@id = 'Software SRAM']", cache)
ssram_entry = create_object(
rtct.RTCTSubtableSoftwareSRAM_v2,
subtable_size = ctypes.sizeof(rtct.RTCTSubtableSoftwareSRAM_v2),
format_or_version = 2,
type = rtct.ACPI_RTCT_V2_TYPE_SoftwareSRAM,
level = int(cache.get("level")),
cache_id = int(cache.get("id"), base=16),
base = int(ssram_cap.find("start").text, base=16),
size = int(ssram_cap.find("size").text),
shared = 0
)
rtct_entries.append(ssram_entry)
if ssram_cap.find("waymask") is not None:
ssram_waymask_entry = create_object(
rtct.RTCTSubtableSSRAMWayMask,
subtable_size = ctypes.sizeof(rtct.RTCTSubtableSSRAMWayMask),
format_or_version = 1,
type = rtct.ACPI_RTCT_V2_TYPE_SSRAM_WayMask,
level = int(cache.get("level")),
cache_id = int(cache.get("id"), base=16),
waymask = int(ssram_cap.find("waymask").text, base=16)
)
rtct_entries.append(ssram_waymask_entry)
with open(dest_path, "wb") as dest:
length = sum(map(ctypes.sizeof, rtct_entries))
common_header.length = length
binary = bytearray().join(map(bytearray, rtct_entries))
checksum = (256 - (sum(binary) % 256)) % 256
binary[9] = checksum
dest.write(binary)
def main(args):
err_dic = {}
(err_dic, params) = acrn_config_utilities.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 = board_etree.getroot()
scenario_etree = lxml.etree.parse(scenario)
scenario_root = scenario_etree.getroot()
allocation_etree = lxml.etree.parse(os.path.join(os.path.dirname(board), "configs", "allocation.xml"))
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(acrn_config_utilities.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']
load_order_node = vm.find('load_order')
if (load_order_node is not None) and (load_order_node.text == 'PRE_LAUNCHED_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_RTCT = False
PRELAUNCHED_RTVM_ID = None
try:
if scenario_root.find('hv/FEATURES/SSRAM/SSRAM_ENABLED').text.strip() == 'y':
PASSTHROUGH_RTCT = True
for vm in scenario_root.findall('vm'):
vm_id = vm.attrib['id']
vm_type_node = vm.find('vm_type')
load_order_node = vm.find('load_order')
if (load_order_node is not None) and (load_order_node.text == 'PRE_LAUNCHED_VM') and (vm_type_node.text == 'RTVM'):
PRELAUNCHED_RTVM_ID = vm_id
break
except:
PASSTHROUGH_RTCT = False
kern_args = acrn_config_utilities.get_leaf_tag_map(scenario, "os_config", "bootargs")
kern_type = acrn_config_utilities.get_leaf_tag_map(scenario, "os_config", "kern_type")
for vm_id, passthru_devices in dict_passthru_devices.items():
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_RTCT is True and vm_id == PRELAUNCHED_RTVM_ID:
passthru_devices.append("RTCT")
gen_rtct(board_etree, scenario_etree, allocation_etree, vm_id, os.path.join(dest_vm_acpi_path, "rtct.aml"))
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 = get_node(f"//processors//thread[cpu_id='{id}']/apic_id/text()", board_etree)
if apic_id is None:
emsg = 'some or all of the processors//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(board_etree, scenario_etree, allocation_etree, vm_id, os.path.join(dest_vm_acpi_path, "dsdt.aml"))
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)