# 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 """ # initialize DRHDx_IGNORE to false dmar_hw_list.hw_ignore[drhd_cnt] = 'false' # 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 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