mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-04-28 19:54:01 +00:00
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>
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)
|