mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-03 04:39:50 +00:00
Commit4a04fcc
("config_tools: skip remapping HW units with no devices under scope") skips hardware remapping units without any device under its scope in the config tools, which turns out to only work if the HV is not parsing the DMAR at runtime. This patch reverts the previous workaround and fixes the previous issue by always initializing `dmar_hw_list.hw_ignore` when parsing DMAR. This ensures that the DRHDx_IGNORE macro will always be emitted while DRHD_COUNT is not impacted. Fixes:4a04fcc
("config_tools: skip remapping HW units with no devices under scope") Tracked-On: #6709 Signed-off-by: Junjie Mao <junjie.mao@intel.com>
402 lines
14 KiB
Python
402 lines
14 KiB
Python
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
|
#
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
#
|
|
|
|
import sys
|
|
import ctypes
|
|
import parser_lib
|
|
|
|
ACPI_DMAR_TYPE = {
|
|
'ACPI_DMAR_TYPE_HARDWARE_UNIT':0,
|
|
'ACPI_DMAR_TYPE_RESERVED_MEMORY':1,
|
|
'ACPI_DMAR_TYPE_ROOT_ATS':2,
|
|
'ACPI_DMAR_TYPE_HARDWARE_AFFINITY':3,
|
|
'ACPI_DMAR_TYPE_NAMESPACE':4,
|
|
'ACPI_DMAR_TYPE_RESERVED':5,
|
|
}
|
|
|
|
ACPI_DEV_SCOPE_TYPE = {
|
|
'ACPI_DMAR_SCOPE_TYPE_NOT_USED':0,
|
|
'ACPI_DMAR_SCOPE_TYPE_ENDPOINT':1,
|
|
'ACPI_DMAR_SCOPE_TYPE_BRIDGE':2,
|
|
'ACPI_DMAR_SCOPE_TYPE_IOAPIC':3,
|
|
'ACPI_DMAR_SCOPE_TYPE_HPET':4,
|
|
'ACPI_DMAR_SCOPE_TYPE_NAMESPACE':5,
|
|
'ACPI_DMAR_SCOPE_TYPE_RESERVED':6,
|
|
}
|
|
|
|
class DmarHeader(ctypes.Structure):
|
|
"""DMAR Header"""
|
|
_pack_ = 1
|
|
_fields_ = [
|
|
('signature', ctypes.c_char*4),
|
|
('length', ctypes.c_uint32),
|
|
('revision', ctypes.c_ubyte),
|
|
('checksum', ctypes.c_ubyte),
|
|
('oem_id', ctypes.c_char*6),
|
|
('oem_table_id', ctypes.c_char*8),
|
|
('oem_revision', ctypes.c_uint32),
|
|
('asl_compiler_id', ctypes.c_char*4),
|
|
('asl_compiler_revision', ctypes.c_uint32),
|
|
('host_addr_width', ctypes.c_ubyte),
|
|
('flags', ctypes.c_ubyte),
|
|
('reserved', ctypes.c_ubyte*10),
|
|
]
|
|
|
|
def style_check_1(self):
|
|
"""Style check if have public method"""
|
|
self._pack_ = 0
|
|
|
|
def style_check_2(self):
|
|
"""Style check if have public method"""
|
|
self._pack_ = 0
|
|
|
|
|
|
class DmarDevScope(ctypes.Structure):
|
|
"""DMAR Device Scope"""
|
|
_pack_ = 1
|
|
_fields_ = [
|
|
('entry_type', ctypes.c_uint8),
|
|
('scope_length', ctypes.c_uint8),
|
|
('reserved', ctypes.c_uint16),
|
|
('enumeration_id', ctypes.c_uint8),
|
|
('bus', ctypes.c_uint8),
|
|
]
|
|
|
|
def style_check_1(self):
|
|
"""Style check if have public method"""
|
|
self._pack_ = 0
|
|
|
|
def style_check_2(self):
|
|
"""Style check if have public method"""
|
|
self._pack_ = 0
|
|
|
|
|
|
class DmarHwUnit(ctypes.Structure):
|
|
"""DMAR Hardware Unit"""
|
|
_pack_ = 1
|
|
_fields_ = [
|
|
('type', ctypes.c_uint16),
|
|
('length', ctypes.c_uint16),
|
|
('flags', ctypes.c_uint8),
|
|
('reserved', ctypes.c_uint8),
|
|
('segment', ctypes.c_uint16),
|
|
('address', ctypes.c_uint64),
|
|
]
|
|
|
|
def style_check_1(self):
|
|
"""Style check if have public method"""
|
|
self._pack_ = 0
|
|
|
|
def style_check_2(self):
|
|
"""Style check if have public method"""
|
|
self._pack_ = 0
|
|
|
|
|
|
class DevScopePath(ctypes.Structure):
|
|
"""DEVICE Scope Path"""
|
|
_pack_ = 1
|
|
_fields_ = [
|
|
("device", ctypes.c_uint8),
|
|
("function", ctypes.c_uint8),
|
|
]
|
|
|
|
def style_check_1(self):
|
|
"""Style check if have public method"""
|
|
self._pack_ = 0
|
|
|
|
def style_check_2(self):
|
|
"""Style check if have public method"""
|
|
self._pack_ = 0
|
|
|
|
|
|
class DmarHwList:
|
|
"""DMAR HW List"""
|
|
def __init__(self):
|
|
self.hw_segment_list = []
|
|
self.hw_flags_list = []
|
|
self.hw_address_list = []
|
|
self.hw_ignore = {}
|
|
|
|
def style_check_1(self):
|
|
"""Style check if have public method"""
|
|
self.hw_ignore = {}
|
|
|
|
def style_check_2(self):
|
|
"""Style check if have public method"""
|
|
self.hw_ignore = {}
|
|
|
|
|
|
class DmarDevList:
|
|
"""DMAR DEV List"""
|
|
def __init__(self):
|
|
self.dev_scope_cnt_list = []
|
|
self.dev_bus_list = []
|
|
self.dev_path_list = []
|
|
self.dev_scope_id_list = []
|
|
self.dev_scope_type_list = []
|
|
|
|
def style_check_1(self):
|
|
"""Style check if have public method"""
|
|
self.dev_bus_list = []
|
|
|
|
def style_check_2(self):
|
|
"""Style check if have public method"""
|
|
self.dev_bus_list = []
|
|
|
|
|
|
class DmarTbl:
|
|
"""DMAR TBL"""
|
|
def __init__(self):
|
|
self.drhd_offset = 0
|
|
self.dmar_drhd = 0
|
|
self.dmar_dev_scope = 0
|
|
self.dev_scope_offset = 0
|
|
self.dev_scope_cnt = 0
|
|
self.path_offset = 0
|
|
|
|
def style_check_1(self):
|
|
"""Style check if have public method"""
|
|
self.path_offset = 0
|
|
|
|
def style_check_2(self):
|
|
"""Style check if have public method"""
|
|
self.path_offset = 0
|
|
|
|
|
|
# TODO: Get board information is independent part of acrn-config tools, it does not get the GPU_SBDF default
|
|
# config from the other part of tools, so hard code the GPU_SBDF to gernerate DRHDx_IGNORE macro
|
|
CONFIG_GPU_SBDF = 0x10
|
|
|
|
PCI_BRIDGE_HEADER = 1
|
|
|
|
class PathDevFun:
|
|
"""Path Device Function meta data"""
|
|
def __init__(self):
|
|
self.path = 0
|
|
self.device = 0
|
|
self.function = 0
|
|
|
|
def style_check_1(self):
|
|
"""Style check if have public method"""
|
|
self.path = 0
|
|
|
|
def style_check_2(self):
|
|
"""Style check if have public method"""
|
|
self.path = 0
|
|
|
|
|
|
def get_secondary_bus(dmar_tbl, tmp_dev, tmp_fun):
|
|
|
|
cmd = "lspci -xxx"
|
|
secondary_bus_str = ''
|
|
found_pci_bdf = False
|
|
pci_bridge_header_type = False
|
|
res = parser_lib.cmd_execute(cmd)
|
|
|
|
while True:
|
|
line = res.stdout.readline().decode("ascii")
|
|
|
|
if not line:
|
|
break
|
|
|
|
if ':' not in line or len(line.strip().split(":")) < 1:
|
|
continue
|
|
|
|
if not found_pci_bdf:
|
|
if '.' not in line.strip():
|
|
continue
|
|
|
|
bus = int(line.strip().split(":")[0], 16)
|
|
dev = int(line.strip().split()[0].split(":")[1].split(".")[0], 16)
|
|
fun = int(line.strip().split()[0].split(":")[1].split(".")[1].strip(), 16)
|
|
if bus == dmar_tbl.dmar_dev_scope.bus and dev == tmp_dev and fun == tmp_fun:
|
|
found_pci_bdf = True
|
|
continue
|
|
else:
|
|
if "00:" == line.strip().split()[0]:
|
|
# PCI Header type stores in 0xE
|
|
if len(line.split()) >= 16 and int(line.strip().split()[15], 16) & 0x7 == PCI_BRIDGE_HEADER:
|
|
pci_bridge_header_type = True
|
|
continue
|
|
|
|
if not pci_bridge_header_type:
|
|
continue
|
|
|
|
# found the pci device, parse the secondary bus
|
|
if "10:" == line.strip().split()[0]:
|
|
# Secondary PCI BUS number stores in 0x18
|
|
secondary_bus_str = line.split()[9]
|
|
found_pci_bdf = False
|
|
pci_bridge_header_type = False
|
|
break
|
|
|
|
# the pci device has secondary bus
|
|
if secondary_bus_str:
|
|
dmar_tbl.dmar_dev_scope.bus = int(secondary_bus_str, 16)
|
|
|
|
|
|
def walk_pci_bus(tmp_pdf, dmar_tbl, dmar_hw_list, drhd_cnt):
|
|
"""Walk Pci bus
|
|
:param tmp_pdf: it is a class what contains path,device,function in dmar device scope region
|
|
:param dmar_tbl: it is a class to describe dmar which contains Device Scope and DRHD
|
|
:param dmar_hw_list: it is a class to describe hardware scope in DMAR table
|
|
:param drhd_cnt: it is a counter to calculate the DRHD in DMAR table
|
|
"""
|
|
# path offset is in end of device spcope
|
|
dmar_tbl.path_offset = dmar_tbl.dev_scope_offset + ctypes.sizeof(DmarDevScope)
|
|
n_cnt = (dmar_tbl.dmar_dev_scope.scope_length - ctypes.sizeof(DmarDevScope)) // 2
|
|
while n_cnt:
|
|
scope_path = DevScopePath.from_address(dmar_tbl.path_offset)
|
|
tmp_pdf.device = scope_path.device
|
|
tmp_pdf.function = scope_path.function
|
|
tmp_pdf.path = (((tmp_pdf.device & 0x1F) << 3) | ((tmp_pdf.function & 0x7)))
|
|
|
|
# walk the secondary pci bus
|
|
get_secondary_bus(dmar_tbl, tmp_pdf.device, tmp_pdf.function)
|
|
|
|
if ((dmar_tbl.dmar_drhd.segment << 16) | (
|
|
dmar_tbl.dmar_dev_scope.bus << 8) | tmp_pdf.path) == CONFIG_GPU_SBDF:
|
|
dmar_hw_list.hw_ignore[drhd_cnt] = 'true'
|
|
|
|
dmar_tbl.path_offset += ctypes.sizeof(DevScopePath)
|
|
n_cnt -= 1
|
|
|
|
|
|
def walk_dev_scope(dmar_tbl, dmar_dev_list, dmar_hw_list, drhd_cnt):
|
|
"""Walk device scope
|
|
:param dmar_tbl: it is a class to describe dmar which contains Device Scope and DRHD
|
|
:param dmar_dev_list: it is a class to describe device scope in DMAR table
|
|
:param dmar_hw_list: it is a class to describe DRHD in DMAR table
|
|
:param drhd_cnt: it is a counter to calculate the DRHD in DMAR table
|
|
"""
|
|
dmar_tbl.dev_scope_offset = dmar_tbl.drhd_offset + ctypes.sizeof(DmarHwUnit)
|
|
scope_end = dmar_tbl.dev_scope_offset + dmar_tbl.dmar_drhd.length
|
|
dmar_tbl.dev_scope_cnt = 0
|
|
|
|
while dmar_tbl.dev_scope_offset < scope_end:
|
|
dmar_tbl.dmar_dev_scope = DmarDevScope.from_address(dmar_tbl.dev_scope_offset)
|
|
if dmar_tbl.dmar_dev_scope.scope_length <= 0:
|
|
break
|
|
if dmar_tbl.dmar_dev_scope.entry_type != \
|
|
ACPI_DEV_SCOPE_TYPE['ACPI_DMAR_SCOPE_TYPE_NOT_USED'] and \
|
|
dmar_tbl.dmar_dev_scope.entry_type < \
|
|
ACPI_DEV_SCOPE_TYPE['ACPI_DMAR_SCOPE_TYPE_RESERVED']:
|
|
dmar_tbl.dev_scope_cnt += 1
|
|
|
|
# get type and id from device scope
|
|
dmar_dev_list.dev_scope_type_list.append(dmar_tbl.dmar_dev_scope.entry_type)
|
|
dmar_dev_list.dev_scope_id_list.append(dmar_tbl.dmar_dev_scope.enumeration_id)
|
|
|
|
# walk the pci bus with path deep, and find the {Device,Function}
|
|
tmp_pdf = PathDevFun()
|
|
walk_pci_bus(tmp_pdf, dmar_tbl, dmar_hw_list, drhd_cnt)
|
|
dmar_dev_list.dev_bus_list.append(dmar_tbl.dmar_dev_scope.bus)
|
|
dmar_dev_list.dev_path_list.append(tmp_pdf.path)
|
|
|
|
dmar_tbl.dev_scope_offset += dmar_tbl.dmar_dev_scope.scope_length
|
|
|
|
|
|
def walk_dmar_table(dmar_tbl, dmar_hw_list, dmar_dev_list, sysnode):
|
|
"""Walk dmar table and get information
|
|
:param dmar_tbl: it is a class to describe dmar which contains Device Scope and DRHD
|
|
:param dmar_hw_list: it is a class to describe hardware scope in DMAR table
|
|
:param dmar_dev_list: it is a class to describe device scope in DMAR table
|
|
:param sysnode: the system device node of acpi table, such as: /sys/firmware/acpi/tables/DMAR
|
|
"""
|
|
data = open(sysnode, 'rb').read()
|
|
buf = ctypes.create_string_buffer(data, len(data))
|
|
addr = ctypes.addressof(buf)
|
|
|
|
# contain the dmar tbl_header
|
|
dmar_tbl_header = DmarHeader.from_address(addr)
|
|
|
|
# cytpes.c_int16.from_address(addr) reade int16 from ad1
|
|
# in end of tbl header is remapping structure(DRHD/sub tbl)
|
|
dmar_tbl.drhd_offset = addr + ctypes.sizeof(DmarHeader)
|
|
drhd_cnt = 0
|
|
while True:
|
|
# get one DRHD type in sub table
|
|
dmar_tbl.dmar_drhd = DmarHwUnit.from_address(dmar_tbl.drhd_offset)
|
|
dmar_type = dmar_tbl.dmar_drhd.type
|
|
dmar_len = dmar_tbl.dmar_drhd.length
|
|
|
|
if dmar_tbl.drhd_offset - addr >= dmar_tbl_header.length:
|
|
break
|
|
|
|
if dmar_type != ACPI_DMAR_TYPE['ACPI_DMAR_TYPE_HARDWARE_UNIT']:
|
|
dmar_tbl.drhd_offset += dmar_len
|
|
continue
|
|
|
|
# initialize DRHDx_IGNORE to false
|
|
dmar_hw_list.hw_ignore[drhd_cnt] = 'false'
|
|
|
|
dmar_hw_list.hw_segment_list.append(dmar_tbl.dmar_drhd.segment)
|
|
dmar_hw_list.hw_flags_list.append(dmar_tbl.dmar_drhd.flags)
|
|
dmar_hw_list.hw_address_list.append(dmar_tbl.dmar_drhd.address)
|
|
|
|
# in end of DRHD/sub tbl header is dev scope, then enumerate the device scope
|
|
walk_dev_scope(dmar_tbl, dmar_dev_list, dmar_hw_list, drhd_cnt)
|
|
dmar_dev_list.dev_scope_cnt_list.append(dmar_tbl.dev_scope_cnt)
|
|
|
|
drhd_cnt += 1
|
|
dmar_tbl.drhd_offset += dmar_len
|
|
|
|
return (dmar_tbl, dmar_hw_list, dmar_dev_list, drhd_cnt)
|
|
|
|
|
|
def write_dmar_data(sysnode, config):
|
|
"""Write the DMAR data to board info
|
|
:param sysnode: the system device node of acpi table, such as: /sys/firmware/acpi/tables/DMAR
|
|
:param config: file pointer that opened for writing board information
|
|
"""
|
|
|
|
dmar_hw_list = DmarHwList()
|
|
dmar_dev_list = DmarDevList()
|
|
dmar_tbl = DmarTbl()
|
|
|
|
(dmar_tbl, dmar_hw_list, dmar_dev_list, drhd_cnt) = walk_dmar_table(
|
|
dmar_tbl, dmar_hw_list, dmar_dev_list, sysnode)
|
|
|
|
print("\t#define DRHD_COUNT {0}U".format(drhd_cnt), file=config)
|
|
print("", file=config)
|
|
prev_dev_scope_num = 0
|
|
for drhd_hw_i in range(drhd_cnt):
|
|
dev_scope_num = dmar_dev_list.dev_scope_cnt_list[drhd_hw_i]
|
|
print("\t#define DRHD"+str(drhd_hw_i)+"_DEV_CNT {0}U".format(
|
|
hex(dmar_dev_list.dev_scope_cnt_list[drhd_hw_i])), file=config)
|
|
print("\t#define DRHD"+str(drhd_hw_i)+"_SEGMENT {0}U".format(
|
|
hex(dmar_hw_list.hw_segment_list[drhd_hw_i])), file=config)
|
|
print("\t#define DRHD"+str(drhd_hw_i)+"_FLAGS {0}U".format(
|
|
hex(dmar_hw_list.hw_flags_list[drhd_hw_i])), file=config)
|
|
print("\t#define DRHD"+str(drhd_hw_i)+"_REG_BASE 0x{:0>2X}UL".format(
|
|
dmar_hw_list.hw_address_list[drhd_hw_i]), file=config)
|
|
if drhd_hw_i in dmar_hw_list.hw_ignore.keys():
|
|
print("\t#define DRHD"+str(drhd_hw_i)+"_IGNORE {0}".format(
|
|
dmar_hw_list.hw_ignore[drhd_hw_i]), file=config)
|
|
for dev_scope_i in range(dev_scope_num):
|
|
print("\t#define DRHD"+str(drhd_hw_i)+"_DEVSCOPE"+str(dev_scope_i),
|
|
file=config, end="")
|
|
print("_TYPE {0}U".format(
|
|
hex(dmar_dev_list.dev_scope_type_list[prev_dev_scope_num + dev_scope_i])),
|
|
file=config)
|
|
print("\t#define DRHD"+str(drhd_hw_i)+"_DEVSCOPE"+str(dev_scope_i),
|
|
file=config, end="")
|
|
print("_ID {0}U".format(
|
|
hex(dmar_dev_list.dev_scope_id_list[prev_dev_scope_num + dev_scope_i])),
|
|
file=config)
|
|
print("\t#define DRHD"+str(drhd_hw_i)+"_DEVSCOPE"+str(dev_scope_i),
|
|
file=config, end="")
|
|
print("_BUS {0}U".format(hex(
|
|
dmar_dev_list.dev_bus_list[prev_dev_scope_num + dev_scope_i])),
|
|
file=config)
|
|
print("\t#define DRHD"+str(drhd_hw_i)+"_DEVSCOPE"+str(dev_scope_i),
|
|
file=config, end="")
|
|
print("_PATH {0}U".format(hex(
|
|
dmar_dev_list.dev_path_list[prev_dev_scope_num + dev_scope_i])),
|
|
file=config)
|
|
|
|
print("", file=config)
|
|
prev_dev_scope_num += dev_scope_num
|