962 lines
40 KiB
Python
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)
|