diff --git a/misc/config_tools/board_inspector/acpiparser/aml/context.py b/misc/config_tools/board_inspector/acpiparser/aml/context.py new file mode 100644 index 000000000..acb943cd5 --- /dev/null +++ b/misc/config_tools/board_inspector/acpiparser/aml/context.py @@ -0,0 +1,334 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import logging +from copy import copy +from math import floor + +from .exception import * +from .stream import Stream + +class NamedDecl: + def __init__(self, name, tree): + self.tree = tree + self.name = name + + def dump(self): + print(f"{self.name}: {self.__class__.__name__}") + +class FieldDecl(NamedDecl): + def __init__(self, name, length, tree): + super().__init__(name, tree) + self.length = length + + def dump(self): + print(f"{self.name}: {self.__class__.__name__}, {self.length} bits") + +class OperationFieldDecl(NamedDecl): + def __init__(self, name, length, tree): + super().__init__(name, tree) + self.region = None + self.offset = None + self.length = length + + def set_location(self, region, offset): + self.region = region + self.offset = offset + + def dump(self): + if self.region and self.offset: + bit_index = self.offset + byte_index = floor(bit_index / 8) + offset_in_byte = bit_index % 8 + print(f"{self.name}: {self.__class__.__name__}, {self.region}: bit {hex(bit_index)} (byte {hex(byte_index)}.{offset_in_byte}), {self.length} bits") + else: + print(f"{self.name}: {self.__class__.__name__}, {self.length} bits") + +class AliasDecl(NamedDecl): + def __init__(self, name, target, tree): + super().__init__(name, tree) + self.name = name + self.target = target + + def dump(self): + print(f"{self.name}: {self.__class__.__name__}, aliasing {self.target}") + +class MethodDecl(NamedDecl): + def __init__(self, name, nargs, tree): + super().__init__(name, tree) + self.nargs = nargs + + def dump(self): + print(f"{self.name}: {self.__class__.__name__}, {self.nargs} args") + +class PredefinedMethodDecl(NamedDecl): + def __init__(self, name, nargs, fn): + super().__init__(name, None) + self.nargs = nargs + self.fn = fn + + def dump(self): + print(f"{self.name}: {self.__class__.__name__}, {self.nargs} args") + +class DeviceDecl(NamedDecl): + def __init__(self, name, tree): + super().__init__(name, tree) + +def predefined_osi(args): + feature = args[0].get() + if feature.startswith("Linux"): + return 0xffffffff + elif feature.startswith("Windows") or \ + feature.startswith("FreeBSD") or \ + feature.startswith("HP-UX") or \ + feature.startswith("OpenVMS"): + return 0 + return 0xffffffff + +class Context: + @staticmethod + def realpath(scope, name): + if name and name.startswith("\\"): + return name + + if name and name.startswith("^"): + parent_count = name.count("^") + assert parent_count <= len(scope) + scope = scope[:-parent_count] + name = name[parent_count:] + + if scope: + if isinstance(scope, list): + if name: + return f"\\{'.'.join(scope)}.{name}" + else: + return f"\\{'.'.join(scope)}" + elif isinstance(scope, str): + if scope == "\\": + return f"\\{name}" + else: + return f"{scope}.{name}" + else: + raise NotImplementedError + else: + if name: + return f"\\{name}" + else: + return f"\\" + + @staticmethod + def parent(scope): + if scope == "\\": + return "\\" + else: + parent = scope[:-4] + if parent.endswith("."): + return parent[:-1] + else: + return parent + + def __init__(self): + self.streams = {} + self.current_stream = None + + # Loaded namespace + self.__symbol_table = {} + self.__devices = [] + + # Context during parsing + self.__current_scope = [] + self.__scope_history = [] + self.__deferred_mode_depth = 0 + + # Context during interpretation + self.__binding_table = {} + self.__op_regions = {} + + # Register predefined objects per section 5.7, ACPI 6.4 + self.register_symbol(NamedDecl("_GL_", None)) + self.register_symbol(PredefinedMethodDecl("_OSI", 1, predefined_osi)) + self.register_symbol(NamedDecl("_OS_", None)) + self.register_symbol(NamedDecl("_REV", None)) + self.register_symbol(NamedDecl("_DLM", None)) + + # Mode switches + self.__skip_external_on_lookup = False + + def switch_stream(self, filepath): + if not filepath in self.streams.keys(): + with open(filepath, "rb") as f: + stream = Stream(f.read()) + self.streams[filepath] = stream + self.current_stream = stream + else: + self.current_stream = self.streams[filepath] + self.current_stream.reset() + + def get_scope(self): + return self.realpath(self.__current_scope, "") + + def change_scope(self, new_scope): + self.__scope_history.append(copy(self.__current_scope)) + if isinstance(new_scope, list): + self.__current_scope = new_scope + elif isinstance(new_scope, str): + if new_scope.startswith("\\"): + self.__current_scope = [i for i in new_scope[1:].split(".") if i] + elif new_scope.startswith("^"): + parent_count = new_scope.count("^") + assert parent_count <= len(self.__current_scope) + self.__current_scope = self.__current_scope[:-parent_count].extend(new_scope.split(".")) + else: + self.__current_scope.extend(new_scope.split(".")) + else: + raise InvalidPath(new_scope) + + def enter_scope(self, name): + self.__scope_history.append(copy(self.__current_scope)) + self.__current_scope.append(name) + + def pop_scope(self): + assert(self.__scope_history) + self.__current_scope = self.__scope_history.pop() + + def __register_symbol(self, symbol): + self.__symbol_table[symbol.name] = symbol + if isinstance(symbol, DeviceDecl): + self.__devices.append(symbol) + + def register_symbol(self, symbol): + symbol.name = self.realpath(self.__current_scope, symbol.name) + if symbol.name in self.__symbol_table.keys(): + old_tree = self.__symbol_table[symbol.name].tree + new_tree = symbol.tree + if old_tree.label != new_tree.label: + if old_tree.label == "DefExternal": + self.__register_symbol(symbol) + elif new_tree.label == "DefExternal": + pass + else: + logging.warning(f"{symbol.name} is redefined as {new_tree.label} (previously was {old_tree.label})") + self.__register_symbol(symbol) + else: + self.__register_symbol(symbol) + + def unregister_object(self, realpath): + sym = self.__symbol_table.pop(realpath, None) + if isinstance(sym, DeviceDecl): + self.__devices.remove(sym) + + def __lookup_symbol_in_parents(self, table, name): + prefix_len = len(self.__current_scope) + while prefix_len >= 0: + path = self.realpath(self.__current_scope[:prefix_len], name) + if path in table: + sym = table[path] + # External object declarations are only for parsing. At + # interpretation time such declarations should not be looked up. + if (not self.__skip_external_on_lookup) or \ + isinstance(sym, PredefinedMethodDecl) or \ + (sym.tree and sym.tree.label != "DefExternal"): + return sym + prefix_len -= 1 + raise KeyError(name) + + def lookup_symbol(self, name): + try: + if name.startswith("\\"): + return self.__symbol_table[name] + elif name.startswith("^") or name.find(".") >= 0: + realpath = self.realpath(self.__current_scope, name) + return self.__symbol_table[realpath] + else: + return self.__lookup_symbol_in_parents(self.__symbol_table, name) + except KeyError: + logging.debug(f"Cannot find definition of {name}") + raise UndefinedSymbol(name, self.get_scope()) + + def has_symbol(self, name): + try: + self.lookup_symbol(name) + return True + except UndefinedSymbol: + return False + + def lookup_symbol_by_tree(self, tree): + result = filter(lambda x: x[1].tree is tree, self.__symbol_table.items()) + try: + return next(result)[1] + except StopIteration: + return None + + def get_fresh_name(self): + current_scope = self.get_scope() + for i in range(0, 10): + name = self.realpath(current_scope, f"_T_{i}") + if not self.lookup_symbol(name): + return name + raise NotImplementedError("Cannot find a proper fresh name") + + @property + def devices(self): + return self.__devices + + def dump_symbols(self): + for k,v in sorted(self.__symbol_table.items()): + v.dump() + + def enter_deferred_mode(self): + self.__deferred_mode_depth += 1 + + def exit_deferred_mode(self): + assert self.__deferred_mode_depth > 0 + self.__deferred_mode_depth -= 1 + + def in_deferred_mode(self): + return (self.__deferred_mode_depth > 0) + + def skip_external_on_lookup(self): + self.__skip_external_on_lookup = True + + def register_operation_region(self, name, op_region): + self.__op_regions[name] = op_region + + def lookup_operation_region(self, name): + try: + if name.startswith("\\"): + return self.__op_regions[name] + elif name.startswith("^") or name.find(".") >= 0: + realpath = self.realpath(self.__current_scope, name) + return self.__op_regions[realpath] + else: + return self.__lookup_symbol_in_parents(self.__op_regions, name) + except KeyError: + return None + + def register_binding(self, name, value): + sym = self.lookup_symbol(name) + logging.debug(f"Bind {sym.name} to {value}") + self.__binding_table[sym.name] = value + + def lookup_binding(self, name): + sym = self.lookup_symbol(name) + try: + return self.__binding_table[sym.name] + except KeyError: + return None + + def dump_bindings(self): + for k in sorted(self.__binding_table.keys()): + v = self.__binding_table[k] + if v: + try: + val = v.get() + if isinstance(val, int): + val = hex(val) + print(k, val) + except NotImplementedError: + print(k, f"({v.__class__.__name__})") + except AttributeError: + print(k, f"(wrong type: {v})") + else: + print(k, "(None)") diff --git a/misc/config_tools/board_inspector/acpiparser/aml/datatypes.py b/misc/config_tools/board_inspector/acpiparser/aml/datatypes.py new file mode 100644 index 000000000..4aef181dd --- /dev/null +++ b/misc/config_tools/board_inspector/acpiparser/aml/datatypes.py @@ -0,0 +1,327 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import mmap +import logging +from math import floor, ceil + +from .exception import * + +class Object: + def get(self): + raise NotImplementedError(self.__class__.__name__) + + def set(self, obj): + raise NotImplementedError(self.__class__.__name__) + + def to_buffer(self): + raise NotImplementedError(self.__class__.__name__) + + def to_decimal_string(self): + raise NotImplementedError(self.__class__.__name__) + + def to_hex_string(self): + raise NotImplementedError(self.__class__.__name__) + + def to_integer(self): + raise NotImplementedError(self.__class__.__name__) + + def to_string(self): + raise NotImplementedError(self.__class__.__name__) + + def get_obj(self): + return self + +class UninitializedObject(Object): + def to_string(self): + return "Uninitialized Object" + +class Buffer(Object): + @staticmethod + def bitmask(to, frm): + return ((1 << (to + 1)) - 1) - ((1 << frm) - 1) + + def __init__(self, data): + assert len(data) > 0 + self.__data = bytearray(data) + self.__fields = {} # name -> (offset, bitwidth) + + @property + def data(self): + return bytes(self.__data) + + def create_field(self, name, offset, bitwidth): + self.__fields[name] = (offset, bitwidth) + + def read_field(self, name): + offset, bitwidth = self.__fields[name] + acc = 0 + acc_bit_count = 0 + bit_idx = offset + bit_remaining = bitwidth + + assert offset + bitwidth <= len(self.__data) * 8, \ + f"Buffer overflow: attempt to access field {name} at bit {offset + bitwidth} while the buffer has only {len(self.__data) * 8} bits" + + # Bits out of byte boundary + if bit_idx % 8 > 0: + byte_idx = floor(bit_idx / 8) + bit_count = (8 - bit_idx % 8) + if bit_count > bit_remaining: + bit_count = bit_remaining + + mask = self.bitmask(bit_idx % 8 + bit_count - 1, bit_idx % 8) + acc = (self.__data[byte_idx] & mask) >> bit_idx % 8 + acc_bit_count += bit_count + bit_idx += bit_count + bit_remaining -= bit_count + + while bit_remaining > 0: + byte_idx = floor(bit_idx / 8) + bit_count = 8 if bit_remaining >= 8 else bit_remaining + + mask = self.bitmask(bit_count - 1, 0) + acc |= (self.__data[byte_idx] & mask) << acc_bit_count + acc_bit_count += bit_count + bit_idx += bit_count + bit_remaining -= bit_count + + return acc + + def write_field(self, name, value): + offset, bitwidth = self.__fields[name] + bit_idx = offset + bit_remaining = bitwidth + + assert offset + bitwidth <= len(self.__data) * 8, \ + f"Buffer overflow: attempt to access field {name} at bit {offset + bitwidth} while the buffer has only {len(self.__data) * 8} bits" + + # Bits out of byte boundary + if bit_idx % 8 > 0: + byte_idx = floor(bit_idx / 8) + bit_count = (8 - bit_idx % 8) + if bit_count > bit_remaining: + bit_count = bit_remaining + + mask = self.bitmask(bit_idx % 8 + bit_count - 1, bit_idx % 8) + v = (value & ((1 << bit_count) - 1)) << (bit_idx % 8) + self.__data[byte_idx] = (v & mask) | (self.__data[byte_idx] & (0xFF - mask)) + + value >>= bit_count + bit_idx += bit_count + bit_remaining -= bit_count + + while bit_remaining > 0: + byte_idx = floor(bit_idx / 8) + bit_count = 8 if bit_remaining >= 8 else bit_remaining + + mask = self.bitmask(bit_count - 1, 0) + v = (value & ((1 << bit_count) - 1)) + self.__data[byte_idx] = (v & mask) | (self.__data[byte_idx] & (0xFF - mask)) + + value >>= bit_count + bit_idx += bit_count + bit_remaining -= bit_count + + def get(self): + return self.__data + + def to_buffer(self): + return self + + def to_hex_string(self): + result = ",".join(map(lambda x:hex(x)[2:], self.__data)) + return String(result) + + def to_integer(self): + acc = 0 + i = min(len(self.__data), 8) - 1 + while i >= 0: + acc <<= 8 + acc |= self.__data[i] + i -= 1 + return Integer(acc) + +class BufferField(Object): + def __init__(self, buf, field): + self.__buf = buf + self.__field = field + + def get(self): + return self.__buf.read_field(self.__field) + + def set(self, obj): + self.__buf.write_field(self.__field, obj.get()) + + def to_integer(self): + return Integer(self.get()) + + def to_string(self): + return "Buffer Field" + +# DebugObject + +class Device(Object): + def __init__(self, sym): + self.__sym = sym + + def get_sym(self): + return self.__sym + +# Event + +class FieldUnit(BufferField): + def to_string(self): + return "Field" + +class Integer(Object): + def __init__(self, value, width=64): + self.__value = value + self.__width = width + + def get(self): + return self.__value + + def set(self, obj): + self.__value = obj.get() + + def to_buffer(self): + assert self.__width % 8 == 0 + data = bytearray() + i = 0 + v = self.__value + while i < self.__width: + data.append(v & 0xff) + v >>= 8 + i += 8 + return Buffer(data) + + def to_decimal_string(self): + return String(str(self.__value)) + + def to_hex_string(self): + return String(hex(self.__value)[2:]) + + def to_integer(self): + return self + +class Method(Object): + def __init__(self, tree): + self.tree = tree + self.name = tree.children[1].children + self.body = tree.children[3] + +class PredefinedMethod(Object): + def __init__(self, fn): + self.fn = fn + +# Mutex + +class ObjectReference(Object): + def __init__(self, obj, index=None): + self.__obj = obj + self.__index = index + + def get(self): + if self.__index is not None: + if isinstance(self.__obj, Package): + return self.__obj.elements[self.__index] + else: + raise NotImplementedError + else: + return self.__obj + + def set(self, obj, index=None): + self.__obj = obj + self.__index = index + +class OperationRegion(Buffer): + def __load_system_memory_space(self, offset, length): + offset_page_aligned = (offset >> 12) << 12 + length_page_aligned = ceil(((offset & 0xFFF) + length) / 0x1000) * 0x1000 + + with open('/dev/mem', 'rb') as f: + mm = mmap.mmap(f.fileno(), length_page_aligned, flags=mmap.MAP_PRIVATE, prot=mmap.PROT_READ, offset=offset_page_aligned) + mm.seek(offset & 0xFFF) + data = mm.read(length) + super().__init__(data) + + def __load_pci_configuration_space(self, bus, device, function, offset, length): + sysfs_path = "/sys/devices/pci0000:%02x/0000:%02x:%02x.%d/config" % (bus, bus, device, function) + try: + with open(sysfs_path, "rb") as f: + f.seek(offset) + data = f.read(length) + super().__init__(data) + except FileNotFoundError: + logging.error(f"Cannot read the configuration space of %02x:%02x.%d from {sysfs_path}. Assume the PCI device does not exist." % (bus, device, function)) + data = bytearray([0xff]) * length + super().__init__(data) + + def __init__(self, bus_id, device_id, name, space, offset, length): + if space == 0x00: # SystemMemory + logging.info(f"Loading system memory space {name}: [{hex(offset)}, {hex(offset + length - 1)}]") + self.__load_system_memory_space(offset, length) + elif space == 0x01: # SystemIO + raise FutureWork("Port I/O operation region") + elif space == 0x02: # PCI_Config + assert offset <= 0xFF and (offset + length) <= 0x100 + # Assume bus is 0 for now + bus = bus_id + device = device_id >> 16 + function = device_id & 0xFF + logging.info(f"Loading PCI configuration space {name}: 00:%02x:%d [{hex(offset)}, {hex(offset + length - 1)}]" % (device, function)) + self.__load_pci_configuration_space(bus, device, function, offset, length) + else: + raise NotImplementedError(f"Cannot load operation region in space {space}") + + def to_string(self): + return "Operation Region" + +class Package(Object): + def __init__(self, elements): + self.__elements = elements + + @property + def elements(self): + return self.__elements + + def to_string(self): + return "Package" + +# PowerResource + +# Processor + +class RawDataBuffer(Object): + def __init__(self, data): + self.__data = data + + def get(self): + return self.__data + +class String(Object): + def __init__(self, s): + self.__s = s + + def get(self): + return self.__s + + def set(self, obj): + self.__s = obj.get() + + def to_decimal_string(self): + return self + + def to_hex_string(self): + return self + + def to_integer(self): + return Integer(int(self.__s, base=16)) + + def to_string(self): + return self + +# ThermalZone diff --git a/misc/config_tools/board_inspector/acpiparser/aml/exception.py b/misc/config_tools/board_inspector/acpiparser/aml/exception.py new file mode 100644 index 000000000..bbb9ba472 --- /dev/null +++ b/misc/config_tools/board_inspector/acpiparser/aml/exception.py @@ -0,0 +1,27 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +class DecodeError(Exception): + def __init__(self, opcode, label): + super().__init__(f"{hex(opcode)} is not a known opcode for {label}") + +class DeferLater(Exception): + def __init__(self, label, seq): + super().__init__(f"{label}: defer parsing of {seq}") + +class ScopeMismatch(Exception): + def __init__(self): + super().__init__(f"scope mismatch") + +class UndefinedSymbol(Exception): + def __init__(self, name, context): + super().__init__(f"{name} is not a defined symbol under {context}") + +class InvalidPath(Exception): + def __init__(self, name): + super().__init__(f"{name} is not a valid ACPI namespace path") + +class FutureWork(Exception): + pass diff --git a/misc/config_tools/board_inspector/acpiparser/aml/grammar.py b/misc/config_tools/board_inspector/acpiparser/aml/grammar.py new file mode 100644 index 000000000..d6f57c741 --- /dev/null +++ b/misc/config_tools/board_inspector/acpiparser/aml/grammar.py @@ -0,0 +1,494 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# Primary opcodes +AML_ZERO_OP = 0x00 +AML_RESERVED_FIELD_PREFIX = 0x00 +AML_NULL_NAME = 0x00 +AML_ONE_OP = 0x01 +AML_ACCESS_FIELD_PREFIX = 0x01 +AML_CONNECT_FIELD_PREFIX = 0x02 +AML_EXTENDED_ACCESS_FIELD_PREFIX = 0x03 +AML_ALIAS_OP = 0x06 +AML_NAME_OP = 0x08 +AML_BYTE_PREFIX = 0x0A +AML_WORD_PREFIX = 0x0B +AML_DWORD_PREFIX = 0x0C +AML_STRING_PREFIX = 0x0D +AML_QWORD_PREFIX = 0x0E +AML_SCOPE_OP = 0x10 +AML_BUFFER_OP = 0x11 +AML_PACKAGE_OP = 0x12 +AML_VAR_PACKAGE_OP = 0x13 +AML_METHOD_OP = 0x14 +AML_EXTERNAL_OP = 0x15 +AML_DUAL_NAME_PREFIX = 0x2E +AML_MULTI_NAME_PREFIX = 0x2F +AML_EXT_OP_PREFIX = 0x5B +AML_FIRST_LOCAL_OP = 0x60 +AML_LOCAL0_OP = 0x60 +AML_LOCAL1_OP = 0x61 +AML_LOCAL2_OP = 0x62 +AML_LOCAL3_OP = 0x63 +AML_LOCAL4_OP = 0x64 +AML_LOCAL5_OP = 0x65 +AML_LOCAL6_OP = 0x66 +AML_LOCAL7_OP = 0x67 +AML_ARG0_OP = 0x68 +AML_ARG1_OP = 0x69 +AML_ARG2_OP = 0x6A +AML_ARG3_OP = 0x6B +AML_ARG4_OP = 0x6C +AML_ARG5_OP = 0x6D +AML_ARG6_OP = 0x6E +AML_STORE_OP = 0x70 +AML_REF_OF_OP = 0x71 +AML_ADD_OP = 0x72 +AML_CONCAT_OP = 0x73 +AML_SUBTRACT_OP = 0x74 +AML_INCREMENT_OP = 0x75 +AML_DECREMENT_OP = 0x76 +AML_MULTIPLY_OP = 0x77 +AML_DIVIDE_OP = 0x78 +AML_SHIFT_LEFT_OP = 0x79 +AML_SHIFT_RIGHT_OP = 0x7A +AML_AND_OP = 0x7B +AML_NAND_OP = 0x7C +AML_OR_OP = 0x7D +AML_NOR_OP = 0x7E +AML_XOR_OP = 0x7F +AML_NOT_OP = 0x80 +AML_FIND_SET_LEFT_BIT_OP = 0x81 +AML_FIND_SET_RIGHT_BIT_OP = 0x82 +AML_DEREF_OF_OP = 0x83 +AML_CONCAT_RES_OP = 0x84 +AML_MOD_OP = 0x85 +AML_NOTIFY_OP = 0x86 +AML_SIZE_OF_OP = 0x87 +AML_INDEX_OP = 0x88 +AML_MATCH_OP = 0x89 +AML_CREATE_DWORD_FIELD_OP = 0x8A +AML_CREATE_WORD_FIELD_OP = 0x8B +AML_CREATE_BYTE_FIELD_OP = 0x8C +AML_CREATE_BIT_FIELD_OP = 0x8D +AML_OBJECT_TYPE_OP = 0x8E +AML_CREATE_QWORD_FIELD_OP = 0x8F +AML_LAND_OP = 0x90 +AML_LOR_OP = 0x91 +AML_LNOT_OP = 0x92 +AML_LEQUAL_OP = 0x93 +AML_LGREATER_OP = 0x94 +AML_LLESS_OP = 0x95 +AML_TO_BUFFER_OP = 0x96 +AML_TO_DECIMAL_STRING_OP = 0x97 +AML_TO_HEX_STRING_OP = 0x98 +AML_TO_INTEGER_OP = 0x99 +AML_TO_STRING_OP = 0x9C +AML_COPY_OBJECT_OP = 0x9D +AML_MID_OP = 0x9E +AML_CONTINUE_OP = 0x9F +AML_IF_OP = 0xA0 +AML_ELSE_OP = 0xA1 +AML_WHILE_OP = 0xA2 +AML_NOOP_OP = 0xA3 +AML_RETURN_OP = 0xA4 +AML_BREAK_OP = 0xA5 +AML_BREAKPOINT_OP = 0xCC +AML_ONES_OP = 0xFF + +# Prefixed opcodes, with the least byte being AML_EXT_OP_PREFIX +AML_MUTEX_OP = 0x015b +AML_EVENT_OP = 0x025b +AML_CONDITIONAL_REF_OF_OP = 0x125b +AML_CREATE_FIELD_OP = 0x135b +AML_LOAD_TABLE_OP = 0x1f5b +AML_LOAD_OP = 0x205b +AML_STALL_OP = 0x215b +AML_SLEEP_OP = 0x225b +AML_ACQUIRE_OP = 0x235b +AML_SIGNAL_OP = 0x245b +AML_WAIT_OP = 0x255b +AML_RESET_OP = 0x265b +AML_RELEASE_OP = 0x275b +AML_FROM_BCD_OP = 0x285b +AML_TO_BCD_OP = 0x295b +AML_UNLOAD_OP = 0x2a5b +AML_REVISION_OP = 0x305b +AML_DEBUG_OP = 0x315b +AML_FATAL_OP = 0x325b +AML_TIMER_OP = 0x335b +AML_REGION_OP = 0x805b +AML_FIELD_OP = 0x815b +AML_DEVICE_OP = 0x825b +AML_PROCESSOR_OP = 0x835b +AML_POWER_RESOURCE_OP = 0x845b +AML_THERMAL_ZONE_OP = 0x855b +AML_INDEX_FIELD_OP = 0x865b +AML_BANK_FIELD_OP = 0x875b +AML_DATA_REGION_OP = 0x885b + + +################################################################################ +# 20.2.1 Table and Table Header Encoding +################################################################################ + +AMLCode = ("DefBlockHeader", "TermObj*") +DefBlockHeader = ("TableSignature", "TableLength", "SpecCompliance", "CheckSum", "OemID", "OemTableID", "OemRevision", "CreatorID", "CreatorRevision") +TableSignature = ("DWordData",) +TableLength = ("DWordData",) +SpecCompliance = ("ByteData",) +CheckSum = ("ByteData",) +OemID = ("TWordData",) +OemTableID = ("QWordData",) +OemRevision = ("DWordData",) +CreatorID = ("DWordData",) +CreatorRevision = ("DWordData",) + +################################################################################ +# 20.2.2 Name Objects Encoding +################################################################################ + +# NameSeg is defined in parser.py +# NameString is defined in parser.py + +SimpleName = ["NameString", "ArgObj", "LocalObj"] +SuperName = ["SimpleName", "DebugObj", "ReferenceTypeOpcode"] +NullName = (AML_NULL_NAME,) +Target = ["SuperName", "NullName"] + +################################################################################ +# 20.2.3 Data Objects Encoding +################################################################################ + +ComputationalData = ["ByteConst", "WordConst", "DWordConst", "QWordConst", "String", "ConstObj", "RevisionOp", "DefBuffer"] +DataObject = ["ComputationalData", "DefPackage", "DefVarPackage"] +DataRefObject = ["DataObject"] + +ByteConst = (AML_BYTE_PREFIX, "ByteData") +WordConst = (AML_WORD_PREFIX, "WordData") +DWordConst = (AML_DWORD_PREFIX, "DWordData") +QWordConst = (AML_QWORD_PREFIX, "QWordData") +# String is defined in parser.py + +ConstObj = ["ZeroOp", "OneOp", "OnesOp"] +# ByteList is defined in parser.py +# ByteData, WordData, DWordData and QWordData are defined in parser.py + +ZeroOp = (AML_ZERO_OP,) +OneOp = (AML_ONE_OP,) +OnesOp = (AML_ONES_OP,) +RevisionOp = (AML_REVISION_OP,) + +################################################################################ +# 20.2.4 Package Length Encoding +################################################################################ + +# PkgLength is defined in parser.py + +################################################################################ +# 20.2.5 Term Objects Encoding +################################################################################ + +Object = ["NameSpaceModifierObj", "NamedObj"] +TermObj = ["Object", "StatementOpcode", "ExpressionOpcode"] +TermList = ("TermObj*",) +TermArg = ["ExpressionOpcode", "DataObject", "ArgObj", "LocalObj"] +# MethodInvocation is defined in parser.py + +# 20.2.5.1 Namespace Modifier Objects Encoding +################################################################################ + +NameSpaceModifierObj = ["DefAlias", "DefName", "DefScope"] + +DefAlias = (AML_ALIAS_OP, "NameString", "NameString") +DefName = (AML_NAME_OP, "NameString", "DataRefObject") +DefScope = (AML_SCOPE_OP, "PkgLength", "NameString", "TermList") + +# 20.2.5.2 Named Objects Encoding +################################################################################ + +NamedObj = ["DefBankField", "DefCreateBitField", "DefCreateByteField", "DefCreateDWordField", "DefCreateField", + "DefCreateQWordField", "DefCreateWordField", "DefDataRegion", "DefDevice", "DefEvent", "DefExternal", + "DefField", "DefIndexField", "DefMethod", "DefMutex", "DefOpRegion", "DefPowerRes", "DefProcessor", + "DefThermalZone"] + +DefBankField = (AML_BANK_FIELD_OP, "PkgLength", "NameString", "NameString", "BankValue", "FieldFlags", "FieldList") +BankValue = ("TermArg",) +FieldFlags = ("ByteData",) +FieldList = ("FieldElement*",) +NamedField = ("NameSeg", "FieldLength") +ReservedField = (AML_RESERVED_FIELD_PREFIX, "FieldLength") +AccessField = (AML_ACCESS_FIELD_PREFIX, "AccessType", "AccessAttrib") +AccessType = ("ByteData",) +AccessAttrib = ("ByteData",) +ConnectFieldDef = ["NameString", "BufferData"] +ConnectField = (AML_CONNECT_FIELD_PREFIX, "ConnectFieldDef") + +DefCreateBitField = (AML_CREATE_BIT_FIELD_OP, "SourceBuff", "BitIndex", "NameString") +SourceBuff = ("TermArg",) +BitIndex = ("TermArg",) +DefCreateByteField = (AML_CREATE_BYTE_FIELD_OP, "SourceBuff", "ByteIndex", "NameString") +ByteIndex = ("TermArg",) +DefCreateDWordField = (AML_CREATE_DWORD_FIELD_OP, "SourceBuff", "ByteIndex", "NameString") +DefCreateField = (AML_CREATE_FIELD_OP, "SourceBuff", "BitIndex", "NumBits", "NameString") +NumBits = ("TermArg",) +DefCreateQWordField = (AML_CREATE_QWORD_FIELD_OP, "SourceBuff", "ByteIndex", "NameString") +DefCreateWordField = (AML_CREATE_WORD_FIELD_OP, "SourceBuff", "ByteIndex", "NameString") + +DefDataRegion = (AML_DATA_REGION_OP, "NameString", "TermArg", "TermArg", "TermArg") + +DefDevice = (AML_DEVICE_OP, "PkgLength", "NameString", "TermList") + +DefEvent = (AML_EVENT_OP, "NameString") + +DefExternal = (AML_EXTERNAL_OP, "NameString", "ObjectType", "ArgumentCount") +ObjectType = ("ByteData",) +ArgumentCount = ("ByteData",) + +DefField = (AML_FIELD_OP, "PkgLength", "NameString", "FieldFlags", "FieldList") + +DefIndexField = (AML_INDEX_FIELD_OP, "PkgLength", "NameString", "NameString", "FieldFlags", "FieldList") + +DefMethod = (AML_METHOD_OP, "PkgLength", "NameString", "MethodFlags", "TermList") +MethodFlags = ("ByteData",) + +DefMutex = (AML_MUTEX_OP, "NameString", "SyncFlags") +SyncFlags = ("ByteData",) + +DefOpRegion = (AML_REGION_OP, "NameString", "RegionSpace", "RegionOffset", "RegionLen") +RegionSpace = ("ByteData",) +RegionOffset = ("TermArg",) +RegionLen = ("TermArg",) + +DefPowerRes = (AML_POWER_RESOURCE_OP, "PkgLength", "NameString", "SystemLevel", "ResourceOrder", "TermList") +SystemLevel = ("ByteData",) +ResourceOrder = ("WordData",) + +DefProcessor = (AML_PROCESSOR_OP, "PkgLength", "NameString", "ProcID", "PblkAddr", "PblkLen", "TermList") +ProcID = ("ByteData",) +PblkAddr = ("DWordData",) +PblkLen = ("ByteData",) + +DefThermalZone = (AML_THERMAL_ZONE_OP, "PkgLength", "NameString", "TermList") + +ExtendedAccessField = (AML_EXTENDED_ACCESS_FIELD_PREFIX, "AccessType", "ExtendedAccessAttrib", "AccessLength") +ExtendedAccessAttrib = ("ByteData",) +AccessLength = ("ByteData",) +FieldElement = ["NamedField", "ReservedField", "AccessField", "ExtendedAccessField", "ConnectField"] + +# 20.2.5.3 Statement Opcodes Encoding +################################################################################ + +StatementOpcode = ["DefBreak", "DefBreakPoint", "DefContinue", "DefFatal", "DefIfElse", "DefNoop", "DefNotify", + "DefRelease", "DefReset", "DefReturn", "DefSignal", "DefSleep", "DefStall", "DefUnload", "DefWhile"] + +DefBreak = (AML_BREAK_OP,) + +DefBreakPoint = (AML_BREAKPOINT_OP,) + +DefContinue = (AML_CONTINUE_OP,) + +DefElse = (AML_ELSE_OP, "PkgLength", "TermList") + +DefFatal = (AML_FATAL_OP, "FatalType", "FatalCode", "FataArg") +FatalType = ("ByteData",) +FatalCode = ("DWordData",) +FatalArg = ("TermArg",) + +DefIfElse = (AML_IF_OP, "PkgLength", "Predicate", "TermList", "DefElse?") +Predicate = ("TermArg",) + +DefNoop = (AML_NOOP_OP,) + +DefNotify = (AML_NOTIFY_OP, "NotifyObject", "NotifyValue") +NotifyObject = ("SuperName",) +NotifyValue = ("TermArg",) + +DefRelease = (AML_RELEASE_OP, "MutexObject") +MutexObject = ("SuperName",) + +DefReset = (AML_RESET_OP, "EventObject") +EventObject = ("SuperName",) + +DefReturn = (AML_RETURN_OP, "ArgObject") +ArgObject = ("TermArg",) + +DefSignal = (AML_SIGNAL_OP, "EventObject") + +DefSleep = (AML_SLEEP_OP, "MsecTime") +MsecTime = ("TermArg",) + +DefStall = (AML_STALL_OP, "UsecTime") +UsecTime = ("TermArg",) + +DefUnload = (AML_UNLOAD_OP, "Target") + +DefWhile = (AML_WHILE_OP, "PkgLength", "Predicate", "TermList") + +# 20.2.5.4 Expression Opcodes Encoding +################################################################################ + +ExpressionOpcode = ["DefAcquire", "DefAdd", "DefAnd", "DefBuffer", "DefConcat", "DefConcatRes", "DefCondRefOf", + "DefCopyObject", "DefDecrement", "DefDerefOf", "DefDivide", "DefFindSetLeftBit", + "DefFindSetRightBit", "DefFromBCD", "DefIncrement", "DefIndex", "DefLAnd", "DefLEqual", + "DefLGreater", "DefLLess", "DefMid", "DefLNot", "DefLoad", "DefLoadTable", "DefLOr", "DefMatch", + "DefMod", "DefMultiply", "DefNAnd", "DefNOr", "DefNot", "DefObjectType", "DefOr", "DefPackage", + "DefVarPackage", "DefRefOf", "DefShiftLeft", "DefShiftRight", "DefSizeOf", "DefStore", + "DefSubtract", "DefTimer", "DefToBCD", "DefToBuffer", "DefToDecimalString", "DefToHexString", + "DefToInteger", "DefToString", "DefWait", "DefXOr", "MethodInvocation" ] + +ReferenceTypeOpcode = ["DefRefOf", "DefDerefOf", "DefIndex"] + +DefAcquire = (AML_ACQUIRE_OP, "MutexObject", "Timeout") +Timeout = ("WordData",) + +DefAdd = (AML_ADD_OP, "Operand", "Operand", "Target") +Operand = ["TermArg"] + +DefAnd = (AML_AND_OP, "Operand", "Operand", "Target") + +DefBuffer = (AML_BUFFER_OP, "PkgLength", "BufferSize", "ByteList") +BufferSize = ("TermArg",) + +DefConcat = (AML_CONCAT_OP, "Data", "Data", "Target") +Data = ("TermArg",) + +DefConcatRes = (AML_CONCAT_RES_OP, "BufData", "BufData", "Target") +BufData = ("TermArg",) + +DefCondRefOf = (AML_CONDITIONAL_REF_OF_OP, "SuperName", "Target") + +DefCopyObject = (AML_COPY_OBJECT_OP, "TermArg", "SimpleName") + +DefDecrement = (AML_DECREMENT_OP, "SuperName") + +DefDerefOf = (AML_DEREF_OF_OP, "ObjReference") +ObjReference = ("TermArg",) + +DefDivide = (AML_DIVIDE_OP, "Dividend", "Divisor", "Remainder", "Quotient") +Dividend = ("TermArg",) +Divisor = ("TermArg",) +Remainder = ("Target",) +Quotient = ("Target",) + +DefFindSetLeftBit = (AML_FIND_SET_LEFT_BIT_OP, "Operand", "Target") +DefFindSetRightBit = (AML_FIND_SET_RIGHT_BIT_OP, "Operand", "Target") + +DefFromBCD = (AML_FROM_BCD_OP, "BCDValue", "Target") +BCDValue = ("TermArg",) + +DefIncrement = (AML_INCREMENT_OP, "SuperName") + +DefIndex = (AML_INDEX_OP, "BuffPkgStrObj", "IndexValue", "Target") +BuffPkgStrObj = ("TermArg",) +IndexValue = ("TermArg",) + +DefLAnd = (AML_LAND_OP, "Operand", "Operand") +DefLEqual = (AML_LEQUAL_OP, "Operand", "Operand") +DefLGreater = (AML_LGREATER_OP, "Operand", "Operand") +# DefLGreaterEqual is equivalent to (AML_LNOT_OP, DefLLess) +DefLLess = (AML_LLESS_OP, "Operand", "Operand") +# DefLLessEqual is equivalent to (AML_LNOT_OP, DefLGreater) +DefLNot = (AML_LNOT_OP, "Operand") +# DefLNotEqual is equivalent to (AML_LNOT_OP, DefLEqual) + +DefLoad = (AML_LOAD_OP, "NameString", "Target") + +DefLoadTable = (AML_LOAD_TABLE_OP, "TermArg", "TermArg", "TermArg", "TermArg", "TermArg", "TermArg") + +DefLOr = (AML_LOR_OP, "Operand", "Operand") + +DefMatch = (AML_MATCH_OP, "SearchPkg", "MatchOpcode", "Operand", "MatchOpcode", "Operand", "StartIndex") +SearchPkg = ("TermArg",) +MatchOpcode = ("ByteData",) +StartIndex = ("TermArg",) + +DefMid = (AML_MID_OP, "MidObj", "TermArg", "TermArg", "Target") +MidObj = ("TermArg",) + +DefMod = (AML_MOD_OP, "Dividend", "Divisor", "Target") + +DefMultiply = (AML_MULTIPLY_OP, "Operand", "Operand", "Target") + +DefNAnd = (AML_NAND_OP, "Operand", "Operand", "Target") +DefNOr = (AML_NOR_OP, "Operand", "Operand", "Target") +DefNot = (AML_NOT_OP, "Operand", "Target") + +DefObjectType = (AML_OBJECT_TYPE_OP, "ObjectTypeContent") +ObjectTypeContent = ["SimpleName", "DebugObj", "DefRefof", "DefDerefof", "DefIndex"] + +DefOr = (AML_OR_OP, "Operand", "Operand", "Target") + +DefPackage = (AML_PACKAGE_OP, "PkgLength", "NumElements", "PackageElementList") +DefVarPackage = (AML_VAR_PACKAGE_OP, "PkgLength", "VarNumElements", "PackageElementList") +NumElements = ("ByteData",) +VarNumElements = ("TermArg",) +PackageElementList = ("PackageElement*",) +PackageElement = ["DataRefObject", "NameString"] + +DefRefOf = (AML_REF_OF_OP, "SuperName") + +DefShiftLeft = (AML_SHIFT_LEFT_OP, "Operand", "ShiftCount", "Target") +ShiftCount = ("TermArg",) +DefShiftRight = (AML_SHIFT_RIGHT_OP, "Operand", "ShiftCount", "Target") + +DefSizeOf = (AML_SIZE_OF_OP, "SuperName") + +DefStore = (AML_STORE_OP, "TermArg", "SuperName") + +DefSubtract = (AML_SUBTRACT_OP, "Operand", "Operand", "Target") + +DefTimer = (AML_TIMER_OP,) + +DefToBCD = (AML_TO_BCD_OP, "Operand", "Target") + +DefToBuffer = (AML_TO_BUFFER_OP, "Operand", "Target") + +DefToDecimalString = (AML_TO_DECIMAL_STRING_OP, "Operand", "Target") + +DefToHexString = (AML_TO_HEX_STRING_OP, "Operand", "Target") + +DefToInteger = (AML_TO_INTEGER_OP, "Operand", "Target") + +DefToString = (AML_TO_STRING_OP, "TermArg", "LengthArg", "Target") +LengthArg = ("TermArg",) + +DefWait = (AML_WAIT_OP, "EventObject", "Operand") + +DefXOr = (AML_XOR_OP, "Operand", "Operand", "Target") + +################################################################################ +# 20.2.6 Miscellaneous Objects Encoding +################################################################################ + +# 20.2.6.1 Arg Objects Encoding +################################################################################ + +ArgObj = ["Arg0Op", "Arg1Op", "Arg2Op", "Arg3Op", "Arg4Op", "Arg5Op", "Arg6Op"] +Arg0Op = (AML_ARG0_OP,) +Arg1Op = (AML_ARG1_OP,) +Arg2Op = (AML_ARG2_OP,) +Arg3Op = (AML_ARG3_OP,) +Arg4Op = (AML_ARG4_OP,) +Arg5Op = (AML_ARG5_OP,) +Arg6Op = (AML_ARG6_OP,) + +# 20.2.6.2 Local Objects Encoding +################################################################################ + +LocalObj = ["Local0Op", "Local1Op", "Local2Op", "Local3Op", "Local4Op", "Local5Op", "Local6Op", "Local7Op"] +Local0Op = (AML_LOCAL0_OP,) +Local1Op = (AML_LOCAL1_OP,) +Local2Op = (AML_LOCAL2_OP,) +Local3Op = (AML_LOCAL3_OP,) +Local4Op = (AML_LOCAL4_OP,) +Local5Op = (AML_LOCAL5_OP,) +Local6Op = (AML_LOCAL6_OP,) +Local7Op = (AML_LOCAL7_OP,) + +# 20.2.6.3 Debug Objects Encoding +################################################################################ + +DebugObj = (AML_DEBUG_OP,) diff --git a/misc/config_tools/board_inspector/acpiparser/aml/interpreter.py b/misc/config_tools/board_inspector/acpiparser/aml/interpreter.py new file mode 100644 index 000000000..57eaf6b2b --- /dev/null +++ b/misc/config_tools/board_inspector/acpiparser/aml/interpreter.py @@ -0,0 +1,585 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +from .context import * +from .datatypes import * +from .tree import Tree, Interpreter + +class ConcreteInterpreter(Interpreter): + class Argument(Object): + def __init__(self, frame, index): + self.__frame = frame + self.__index = index + + def get(self): + return self.__frame.arg_objs[self.__index].get() + + def to_buffer(self): + return self.__frame.arg_objs[self.__index].to_buffer() + + def to_decimal_string(self): + return self.__frame.arg_objs[self.__index].to_decimal_string() + + def to_hex_string(self): + return self.__frame.arg_objs[self.__index].to_hex_string() + + def to_integer(self): + return self.__frame.arg_objs[self.__index].to_integer() + + def to_string(self): + return self.__frame.arg_objs[self.__index].to_string() + + def get_obj(self): + return self.__frame.arg_objs[self.__index] + + class LocalVariable(Object): + def __init__(self, frame, index): + self.__frame = frame + self.__index = index + + def get(self): + return self.__frame.local_objs[self.__index].get() + + def set(self, obj): + self.__frame.local_objs[self.__index] = obj + + def to_buffer(self): + return self.__frame.local_objs[self.__index].to_buffer() + + def to_decimal_string(self): + return self.__frame.local_objs[self.__index].to_decimal_string() + + def to_hex_string(self): + return self.__frame.local_objs[self.__index].to_hex_string() + + def to_integer(self): + return self.__frame.local_objs[self.__index].to_integer() + + def to_string(self): + return self.__frame.local_objs[self.__index].to_string() + + def get_obj(self): + return self.__frame.local_objs[self.__index] + + class StackFrame: + def __init__(self, method, args): + self.method = method + self.arg_objs = args + [UninitializedObject()] * (7 - len(args)) + self.local_objs = [UninitializedObject()] * 8 + self.return_value = UninitializedObject() + + def __init__(self, context): + super().__init__(context) + + self.operation_regions = {} + self.stack = [] + + self.to_break = False + self.to_continue = False + + def interpret_method_call(self, name, *args): + stack_depth_before = len(self.stack) + name_string = Tree("NameString", name) + name_string.scope = self.context.parent(name) + pseudo_invocation = Tree("MethodInvocation", [name_string]) + try: + val = self.interpret(pseudo_invocation) + except: + self.stack = self.stack[:stack_depth_before] + raise + assert len(self.stack) == stack_depth_before + return val + + def dump(self): + def format_obj(obj): + if isinstance(obj, UninitializedObject): + return "None" + elif isinstance(obj, Object): + return str(obj.get()) + else: + return str(obj) + print("Stack:") + for idx, frame in enumerate(self.stack): + arg = ', '.join(map(format_obj, frame.arg_objs)) + local = ', '.join(map(format_obj, frame.local_objs)) + print(f" {idx}@{frame.method}: args: [{arg}] locals: [{local}]") + print("Binding:") + self.context.dump_bindings() + + # 20.2.2 Name Objects Encoding + def NullName(self, tree): + return None + + def NameString(self, tree): + name = tree.children + obj = self.context.lookup_binding(name) + if not obj: + sym = self.context.lookup_symbol(name) + if isinstance(sym, PredefinedMethodDecl): + obj = PredefinedMethod(sym.fn) + realpath = self.context.realpath("\\", name) + self.context.register_binding(realpath, obj) + else: + obj = self.interpret(sym.tree) + self.context.register_binding(name, obj) + return obj + + # 20.2.3 Data Objects Encoding + def ByteList(self, tree): + return RawDataBuffer(tree.children) + + def ByteConst(self, tree): + return self.interpret(tree.children[0]) + + def WordConst(self, tree): + return self.interpret(tree.children[0]) + + def DWordConst(self, tree): + return self.interpret(tree.children[0]) + + def QWordConst(self, tree): + return self.interpret(tree.children[0]) + + def String(self, tree): + return String(tree.children) + + def ByteData(self, tree): + return Integer(tree.children) + + def WordData(self, tree): + return Integer(tree.children) + + def DWordData(self, tree): + return Integer(tree.children) + + def QWordData(self, tree): + return Integer(tree.children) + + def ZeroOp(self, tree): + return Integer(0x00) + + def OneOp(self, tree): + return Integer(0x01) + + # 20.2.5 Term Objects Encoding + def TermList(self, tree): + for child in tree.children: + self.interpret(child) + if self.to_break or self.to_continue: + break + return None + + def MethodInvocation(self, tree): + self.context.change_scope(tree.children[0].scope) + value = self.interpret(tree.children[0]) + self.context.pop_scope() + + if isinstance(value, Method): + # Evaluate arguments + args = list(map(self.interpret, tree.children[1:])) + + # Switch to the scope of the callee + realpath = self.context.realpath(value.tree.scope, value.name) + assert realpath + self.context.change_scope(realpath) + + # Evaluate the statements of the callee + self.stack.append(self.StackFrame(realpath, args)) + logging.debug(f"Calling {realpath} with args {args}") + self.interpret(value.body) + frame = self.stack.pop() + self.context.pop_scope() + + # Return the return value of the callee + return frame.return_value + elif isinstance(value, PredefinedMethod): + # Evaluate arguments + args = list(map(self.interpret, tree.children[1:])) + + # Invoke the predefined function in Python + return Integer(value.fn(args)) + else: + assert value == None or isinstance(value, Object), \ + f"{tree.children[0].children} evaluates to a non-object value {value}" + return value + + # 20.2.5.1 Namespace Modifier Objects Encoding + def DefAlias(self, tree): + return None + + def DefName(self, tree): + self.context.change_scope(tree.children[0].scope) + name = tree.children[0].children + obj = self.context.lookup_binding(name) + if not obj: + obj = self.interpret(tree.children[1]) + self.context.register_binding(name, obj) + self.context.pop_scope() + return obj + + # 20.2.5.2 Named Objects Encoding + def NamedField(self, tree): + name = tree.children[0].children + sym = self.context.lookup_symbol(self.context.realpath(tree.scope, name)) + assert isinstance(sym, OperationFieldDecl) + assert sym.region, f"Field {sym.name} does not belong to any operation region." + buf = self.context.lookup_operation_region(sym.region) + if not buf: + buf = self.interpret(self.context.lookup_symbol(sym.region).tree) + buf.create_field(name, sym.offset, sym.length) + field = BufferField(buf, name) + return field + + def create_field(self, tree, bitwidth, name_idx): + buf = self.interpret(tree.children[0]) + assert isinstance(buf, Buffer) + index = self.interpret(tree.children[1]).get() + name = tree.children[name_idx].children + if bitwidth == 1 or name_idx == 3: + buf.create_field(name, index, bitwidth) + else: + buf.create_field(name, index * 8, bitwidth) + obj = BufferField(buf, name) + self.context.register_binding(name, obj) + return obj + + def DefCreateBitField(self, tree): + return self.create_field(tree, 1, 2) + + def DefCreateByteField(self, tree): + return self.create_field(tree, 8, 2) + + def DefCreateWordField(self, tree): + return self.create_field(tree, 16, 2) + + def DefCreateDWordField(self, tree): + return self.create_field(tree, 32, 2) + + def DefCreateQWordField(self, tree): + return self.create_field(tree, 64, 2) + + def DefCreateField(self, tree): + numbits = self.interpret(tree.children[2]).get() + return self.create_field(tree, numbits, 3) + + def DefDevice(self, tree): + name = tree.children[1].children + fullpath = self.context.realpath(tree.scope, name) + sym = self.context.lookup_symbol(fullpath) + return Device(sym) + + def DefExternal(self, tree): + logging.info(f"The loaded tables do not have a definition of {tree.children[0].children}") + return None + + def DefField(self, tree): + # Fields of operation regions are evaluated when they are used. + return None + + def DefMethod(self, tree): + return Method(tree) + + def DefOpRegion(self, tree): + name = tree.children[0].children + sym = self.context.lookup_symbol(self.context.realpath(tree.scope, name)) + + space = self.interpret(tree.children[1]).get() + offset = self.interpret(tree.children[2]).get() + length = self.interpret(tree.children[3]).get() + assert isinstance(space, int) and isinstance(offset, int) and (length, int) + + # For PCI configuration space, try to invoke _ADR (if any) of the device containing the region to get the device + # identifier + bus_id = None + device_id = None + if space == 0x2: # PCI_Config + self.context.change_scope(tree.scope) + device_path = self.context.parent(sym.name) + bus_id = self.interpret_method_call(f"_BBN").get() + device_id = self.interpret_method_call(f"{device_path}._ADR").get() + self.context.pop_scope() + + op_region = OperationRegion(bus_id, device_id, sym.name, space, offset, length) + self.context.register_operation_region(sym.name, op_region) + return op_region + + # 20.2.5.3 Statement Opcodes Encoding + def DefBreak(self, tree): + self.to_break = True + return None + + def DefContinue(self, tree): + self.to_continue = True + return None + + def DefElse(self, tree): + self.interpret(tree.children[1]) + return None + + def DefIfElse(self, tree): + cond = self.interpret(tree.children[1]) + if cond.get(): + self.interpret(tree.children[2]) + else: + if len(tree.children) == 4: + self.interpret(tree.children[3]) + return None + + def DefRelease(self, tree): + return None + + def DefReturn(self, tree): + obj = self.interpret(tree.children[0]) + if isinstance(obj, (self.Argument, self.LocalVariable)): + obj = obj.get_obj() + self.stack[-1].return_value = obj + return None + + def DefSignal(self, tree): + # Skip + return None + + def DefWhile(self, tree): + while self.interpret(tree.children[1]).get() != 0: + self.interpret(tree.children[2]) + if self.to_break: + self.to_break = False + break + return None + + # 20.2.5.4 Expression Opcodes Encoding + def __eval_binary_op(self, tree, op): + lhs = self.interpret(tree.children[0]) + rhs = self.interpret(tree.children[1]) + res = Integer(op(lhs.get(), rhs.get())) + if len(tree.children) >= 3: + target = self.interpret(tree.children[2]) + if target: + target.set(res) + return res + + def DefAcquire(self, tree): + # Pretend that the mutex is acquired + return Integer(0x1) + + def DefAdd(self, tree): + return self.__eval_binary_op(tree, lambda x,y: x + y) + + def DefAnd(self, tree): + return self.__eval_binary_op(tree, lambda x,y: x & y) + + def DefBuffer(self, tree): + size = self.interpret(tree.children[1]).get() + assert isinstance(size, int) + data = self.interpret(tree.children[2]).get() + if len(data) < size: + data = data + bytes(size - len(data)) + return Buffer(data) + + def DefConcat(self, tree): + source1 = self.interpret(tree.children[0]) + source2 = self.interpret(tree.children[1]) + if isinstance(source1, Integer): + data = bytearray() + data.extend(source1.to_buffer().get()) + data.extend(source2.to_integer().to_buffer().get()) + result = Buffer(data) + elif isinstance(source1, String): + data = source1.get() + data += source2.to_string().get() + result = String(data) + elif isinstance(source1, Buffer): + data = bytearray() + data.extend(source1.get()) + data.extend(source2.to_buffer().get()) + result = Buffer(data) + else: + data = source1.to_string().get() + source2.to_string().get() + result = String(data) + target = self.interpret(tree.children[2]) + if target: + target.set(result) + return result + + def DefConcatRes(self, tree): + data = bytearray() + source1 = self.interpret(tree.children[0]) + data.extend(source1.to_buffer().get()) + source2 = self.interpret(tree.children[1]) + data.extend(source2.to_buffer().get()) + result = Buffer(data) + target = self.interpret(tree.children[2]) + if target: + target.set(result) + return result + + def DefCondRefOf(self, tree): + try: + source = self.interpret(tree.children[0]) + if source is not None: + target = self.interpret(tree.children[1]) + if target: + target.set(ObjectReference(source)) + return Integer(1) + else: + return Integer(0) + except UndefinedSymbol: + return Integer(0) + + def DefDecrement(self, tree): + obj = self.interpret(tree.children[0]) + obj.set(Integer(obj.get() - 1)) + return None + + def DefDerefOf(self, tree): + ref = self.interpret(tree.children[0]) + if isinstance(ref, (self.Argument, self.LocalVariable)): + ref = ref.get_obj() + if isinstance(ref, ObjectReference): + return ref.get() + else: + logging.warn(f"Attempt to dereference an object of type {ref.__class__.__name__}") + return ref + + def DefIncrement(self, tree): + obj = self.interpret(tree.children[0]) + obj.set(Integer(obj.get() + 1)) + return None + + def DefIndex(self, tree): + obj = self.interpret(tree.children[0]) + index = self.interpret(tree.children[1]) + target = self.interpret(tree.children[2]) + ret = ObjectReference(obj.get_obj(), index.get()) + if target: + target.set(ret) + return ret + + def DefLAnd(self, tree): + return self.__eval_binary_op(tree, lambda x,y: 1 if x and y else 0) + + def DefLEqual(self, tree): + return self.__eval_binary_op(tree, lambda x,y: x == y) + + def DefLGreater(self, tree): + return self.__eval_binary_op(tree, lambda x,y: x > y) + + def DefLLess(self, tree): + return self.__eval_binary_op(tree, lambda x,y: x < y) + + def DefLNot(self, tree): + operand = self.interpret(tree.children[0]).get() + return Integer(1 if not operand else 0) + + def DefLOr(self, tree): + return self.__eval_binary_op(tree, lambda x,y: 1 if x or y else 0) + + def DefMod(self, tree): + return self.__eval_binary_op(tree, lambda x,y: x % y) + + def DefMultiply(self, tree): + return self.__eval_binary_op(tree, lambda x,y: x * y) + + def DefNAnd(self, tree): + return self.__eval_binary_op(tree, lambda x,y: ~(x & y)) + + def DefNOr(self, tree): + return self.__eval_binary_op(tree, lambda x,y: ~(x | y)) + + def DefNot(self, tree): + operand = self.interpret(tree.children[0]) + target = self.interpret(tree.children[1]) + ret = Integer(~operand.get()) + if target: + target.set(ret) + return ret + + def DefOr(self, tree): + return self.__eval_binary_op(tree, lambda x,y: x | y) + + def DefPackage(self, tree): + elements = list(map(lambda x: self.interpret(x), tree.children[2].children)) + return Package(elements) + + def DefRefOf(self, tree): + obj = self.interpret(tree.children[0]) + return ObjectReference(obj) + + def DefShiftLeft(self, tree): + return self.__eval_binary_op(tree, lambda x,y: x << y) + + def DefShiftRight(self, tree): + return self.__eval_binary_op(tree, lambda x,y: x >> y) + + def DefStore(self, tree): + obj = self.interpret(tree.children[0]) + target = self.interpret(tree.children[1]) + target.set(obj) + return None + + def DefSubtract(self, tree): + return self.__eval_binary_op(tree, lambda x,y: x - y) + + def DefToHexString(self, tree): + operand = self.interpret(tree.children[0]) + result = operand.to_hex_string() + target = self.interpret(tree.children[1]) + if target: + target.set(result) + return result + + def DefToInteger(self, tree): + operand = self.interpret(tree.children[0]) + result = operand.to_integer() + target = self.interpret(tree.children[1]) + if target: + target.set(result) + return result + + # 20.2.6.2 Local Objects Encoding + def Arg0Op(self, tree): + return self.Argument(self.stack[-1], 0) + + def Arg1Op(self, tree): + return self.Argument(self.stack[-1], 1) + + def Arg2Op(self, tree): + return self.Argument(self.stack[-1], 2) + + def Arg3Op(self, tree): + return self.Argument(self.stack[-1], 3) + + def Arg4Op(self, tree): + return self.Argument(self.stack[-1], 4) + + def Arg5Op(self, tree): + return self.Argument(self.stack[-1], 5) + + def Arg6Op(self, tree): + return self.Argument(self.stack[-1], 6) + + def Local0Op(self, tree): + return self.LocalVariable(self.stack[-1], 0) + + def Local1Op(self, tree): + return self.LocalVariable(self.stack[-1], 1) + + def Local2Op(self, tree): + return self.LocalVariable(self.stack[-1], 2) + + def Local3Op(self, tree): + return self.LocalVariable(self.stack[-1], 3) + + def Local4Op(self, tree): + return self.LocalVariable(self.stack[-1], 4) + + def Local5Op(self, tree): + return self.LocalVariable(self.stack[-1], 5) + + def Local6Op(self, tree): + return self.LocalVariable(self.stack[-1], 6) + + def Local7Op(self, tree): + return self.LocalVariable(self.stack[-1], 7) diff --git a/misc/config_tools/board_inspector/acpiparser/aml/parser.py b/misc/config_tools/board_inspector/acpiparser/aml/parser.py new file mode 100644 index 000000000..c1a65bf98 --- /dev/null +++ b/misc/config_tools/board_inspector/acpiparser/aml/parser.py @@ -0,0 +1,537 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import logging + +from . import grammar +from .context import * +from .exception import * +from .tree import Tree, Transformer + +class Factory: + @staticmethod + def hook_pre(context, tree): + pass + + @staticmethod + def hook_named(context, tree, name): + pass + + @staticmethod + def hook_post(context, tree): + pass + + def __init__(self): + self.level = 0 + self.label = "unknown" + + def mark_begin(self): + if hasattr(self, "seq") and len(self.seq) > 1: + logging.debug(f"%s-> {self.label}" % (" " * self.level)) + self.level += 1 + + def mark_end(self): + self.level -= 1 + if hasattr(self, "seq") and len(self.seq) > 1: + logging.debug(f"%s<- {self.label}" % (" " * self.level)) + + def match(self, context, stream, tree): + raise NotImplementedError + + def parse(self, context, tree): + self.mark_begin() + tree.label = self.label + tree.scope = context.get_scope() + self.hook_pre(context, tree) + try: + self.match(context, context.current_stream, tree) + except Exception as e: + self.hook_post(context, tree) + self.mark_end() + raise + + self.hook_post(context, tree) + self.mark_end() + return tree + + def opcodes(self): + raise NotImplementedError + +################################################################################ +# 20.2.2 Name Objects Encoding +################################################################################ + +class NameSegFactory(Factory): + def __init__(self): + super().__init__() + self.__opcodes = [] + for i in range(ord('A'), ord('Z') + 1): + self.__opcodes.append(i) + self.__opcodes.append(ord('_')) + self.label = "NameSeg" + + def match(self, context, stream, tree): + tree.children = stream.get_fixed_length_string(4) + + def opcodes(self): + return self.__opcodes + +NameSeg = NameSegFactory() + +class NameStringFactory(Factory): + def __init__(self): + super().__init__() + self.label = "NameString" + self.__opcodes = [] + for i in range(ord('A'), ord('Z') + 1): + self.__opcodes.append(i) + self.__opcodes.extend([ord('_'), ord('\\'), ord('^'), grammar.AML_DUAL_NAME_PREFIX, grammar.AML_MULTI_NAME_PREFIX]) + + def match(self, context, stream, tree): + acc = "" + + # Namespace prefixes + char = stream.get_char() + while char in ["\\", "^"]: + acc += char + char = stream.get_char() + + # Object name + if ord(char) == grammar.AML_DUAL_NAME_PREFIX: + if acc and acc[-1] not in ["\\", "^"]: + acc += "." + acc += stream.get_fixed_length_string(4) + acc += "." + acc += stream.get_fixed_length_string(4) + elif ord(char) == grammar.AML_MULTI_NAME_PREFIX: + seg_count = stream.get_integer(1) + for i in range(0, seg_count): + if acc and acc[-1] not in ["\\", "^"]: + acc += "." + acc += stream.get_fixed_length_string(4) + elif char == "\x00": # NullName + pass + else: # NameSeg + stream.seek(-1) + acc += stream.get_fixed_length_string(4) + + tree.children = acc + + def opcodes(self): + return self.__opcodes + +NameString = NameStringFactory() + +################################################################################ +# 20.2.3 Data Objects Encoding +################################################################################ + +class ConstDataFactory(Factory): + def __init__(self, label, width): + super().__init__() + self.label = label + self.width = width + + def match(self, context, stream, tree): + tree.children = stream.get_integer(self.width) + return tree + + def opcodes(self): + return None + +ByteData = ConstDataFactory("ByteData", 1) +WordData = ConstDataFactory("WordData", 2) +DWordData = ConstDataFactory("DWordData", 4) +TWordData = ConstDataFactory("TWordData", 6) +QWordData = ConstDataFactory("QWordData", 8) + +class StringFactory(Factory): + def __init__(self): + super().__init__() + self.label = "String" + + def match(self, context, stream, tree): + assert stream.get_opcode()[0] == grammar.AML_STRING_PREFIX + + tree.children = stream.get_string() + return tree + + def opcodes(self): + return [grammar.AML_STRING_PREFIX] + +String = StringFactory() + +class ByteListFactory(Factory): + def __init__(self): + super().__init__() + self.label = "ByteList" + + def match(self, context, stream, tree): + tree.children = stream.get_buffer() + stream.pop_scope() + +ByteList = ByteListFactory() + +################################################################################ +# 20.2.4 Package Length Encoding +################################################################################ + +class PkgLengthFactory(Factory): + @staticmethod + def get_package_length(byte_count, value): + if byte_count == 0: + total_size = (value & 0x3F) + else: + total_size = value & 0x0F + for i in range(1, byte_count + 1): + byte = (value & (0xFF << (i * 8))) >> (i * 8) + total_size |= (byte << (i * 8 - 4)) + return total_size + + def __init__(self, label, create_new_scope): + super().__init__() + self.label = label + self.create_new_scope = create_new_scope + + def match(self, context, stream, tree): + pkg_lead_byte = stream.peek_integer(1) + byte_count = pkg_lead_byte >> 6 + assert byte_count <= 3 + + tree.children = self.get_package_length(byte_count, stream.get_integer(byte_count + 1)) + + if self.create_new_scope: + remaining = tree.children - byte_count - 1 + stream.push_scope(remaining) + tree.package_range = (stream.current, remaining) + return tree + +PkgLength = PkgLengthFactory("PkgLength", True) +FieldLength = PkgLengthFactory("FieldLength", False) + +################################################################################ +# 20.2.5 Term Objects Encoding +################################################################################ + +class MethodInvocationFactory(Factory): + def __init__(self): + super().__init__() + self.__opcodes = None + self.label = "MethodInvocation" + + def match(self, context, stream, tree): + child_namestring = Tree() + globals()["NameString"].parse(context, child_namestring) + tree.append_child(child_namestring) + + sym = context.lookup_symbol(child_namestring.children) + if isinstance(sym, (MethodDecl, PredefinedMethodDecl)): + for i in range(0, sym.nargs): + child_arg = Tree() + globals()["TermArg"].parse(context, child_arg) + tree.append_child(child_arg) + + return tree + + def opcodes(self): + if not self.__opcodes: + self.__opcodes = globals()["NameString"].opcodes() + return self.__opcodes + +MethodInvocation = MethodInvocationFactory() + +################################################################################ +# Infrastructure Factories +################################################################################ + +class SequenceFactory(Factory): + def __init__(self, label, seq): + super().__init__() + self.label = label + self.seq = seq + self.__opcodes = None + + def match(self, context, stream, tree): + # When a TermList is empty, the stream has already come to the end of the current scope here. Do not attempt to + # peek the next opcode in such cases. + if stream.at_end() and \ + (self.seq[0][-1] in ["*", "?"]): + stream.pop_scope() + return tree + + opcode, opcode_width = stream.peek_opcode() + package_end = 0 + + # Under any case this function shall maintain the balance of stream scopes. The following flags indicate the + # cleanup actions upon exceptions. + to_recover_from_deferred_mode = False + to_pop_stream_scope = False + + for i,elem in enumerate(self.seq): + pos = stream.current + try: + if isinstance(elem, int): # The leading opcode + assert elem == opcode + stream.seek(opcode_width) + elif elem.endswith("*"): + elem = elem[:-1] + factory = globals()[elem] + while not stream.at_end(): + child = Tree() + factory.parse(context, child) + tree.append_child(child) + stream.pop_scope() + elif elem.endswith("?"): + elem = elem[:-1] + factory = globals()[elem] + if not stream.at_end(): + sub_opcode, _ = stream.peek_opcode() + if sub_opcode in factory.opcodes(): + child = Tree() + factory.parse(context, child) + tree.append_child(child) + else: + factory = globals()[elem] + child = Tree() + factory.parse(context, child) + tree.append_child(child) + if child.label == "PkgLength": + to_pop_stream_scope = True + if child.package_range: + package_end = child.package_range[0] + child.package_range[1] + context.enter_deferred_mode() + to_recover_from_deferred_mode = True + elif child.label == "NameString": + self.hook_named(context, tree, child.children) + except (DecodeError, DeferLater, ScopeMismatch, UndefinedSymbol) as e: + if to_pop_stream_scope: + stream.pop_scope(force=True) + if to_recover_from_deferred_mode: + tree.deferred_range = (pos, package_end - pos) + tree.context_scope = context.get_scope() + tree.factory = SequenceFactory(f"{self.label}.deferred", self.seq[i:]) + stream.seek(package_end, absolute=True) + break + else: + raise e + + if to_recover_from_deferred_mode: + context.exit_deferred_mode() + return tree + + def opcodes(self): + if not self.__opcodes: + if isinstance(self.seq[0], int): + self.__opcodes = [self.seq[0]] + else: + self.__opcodes = globals()[self.seq[0]].opcodes() + return self.__opcodes + +class OptionFactory(Factory): + def __init__(self, label, opts): + super().__init__() + self.label = label + self.opts = opts + self.__opcodes = None + + def match(self, context, stream, tree): + opcode, _ = stream.peek_opcode() + + for opt in self.opts: + factory = globals()[opt] + matched_opcodes = factory.opcodes() + if matched_opcodes is None or opcode in matched_opcodes: + child = Tree() + tree.append_child(child) + factory.parse(context, child) + return tree + + raise DecodeError(opcode, self.label) + + def opcodes(self): + if not self.__opcodes: + self.__opcodes = [] + for opt in self.opts: + self.__opcodes.extend(globals()[opt].opcodes()) + return self.__opcodes + +class DeferredExpansion(Transformer): + def __init__(self, context): + super().__init__() + self.context = context + + nodes = ["DefScope", "DefDevice", "DefMethod", "DefPowerRes", "DefProcessor", "DefThermalZone", + "DefIfElse", "DefElse", "DefWhile"] + + for i in nodes: + setattr(self, i, self.__expand_deferred_range) + + def __expand_deferred_range(self, tree): + if tree.deferred_range: + start, size = tree.deferred_range + self.context.current_stream.reset() + self.context.current_stream.seek(start, absolute=True) + self.context.current_stream.push_scope(size) + + aux_tree = Tree() + self.context.change_scope(tree.context_scope) + try: + tree.factory.parse(self.context, aux_tree) + tree.children.extend(aux_tree.children) + tree.deferred_range = None + tree.factory = None + except (DecodeError, DeferLater, ScopeMismatch, UndefinedSymbol) as e: + logging.debug(f"expansion of {tree.label} at {hex(tree.deferred_range[0])} failed due to: " + str(e)) + + self.context.pop_scope() + + return tree + +################################################################################ +# Hook functions +################################################################################ + +def DefAlias_hook_post(context, tree): + target = tree.children[0].children + name = tree.children[1].children + sym = AliasDecl(name, target, tree) + context.register_symbol(sym) + +def DefName_hook_named(context, tree, name): + sym = NamedDecl(name, tree) + context.register_symbol(sym) + +def DefScope_hook_named(context, tree, name): + context.change_scope(name) + +def DefScope_hook_post(context, tree): + context.pop_scope() + + +def DefCreateBitField_hook_named(context, tree, name): + name = tree.children[2].children + sym = FieldDecl(name, 1, tree) + context.register_symbol(sym) + +def DefCreateByteField_hook_named(context, tree, name): + name = tree.children[2].children + sym = FieldDecl(name, 8, tree) + context.register_symbol(sym) + +def DefCreateDWordField_hook_named(context, tree, name): + name = tree.children[2].children + sym = FieldDecl(name, 32, tree) + context.register_symbol(sym) + +def DefCreateField_hook_named(context, tree, name): + name = tree.children[3].children + sym = FieldDecl(name, 0, tree) + context.register_symbol(sym) + +def DefCreateQWordField_hook_named(context, tree, name): + name = tree.children[2].children + sym = FieldDecl(name, 64, tree) + context.register_symbol(sym) + +def DefCreateWordField_hook_named(context, tree, name): + name = tree.children[2].children + sym = FieldDecl(name, 16, tree) + context.register_symbol(sym) + +def DefDevice_hook_named(context, tree, name): + sym = DeviceDecl(name, tree) + context.register_symbol(sym) + context.enter_scope(name) + +def DefDevice_hook_post(context, tree): + context.pop_scope() + +def DefExternal_hook_post(context, tree): + name = tree.children[0].children + ty = tree.children[1].children[0].children + nargs = tree.children[2].children[0].children + + if ty == 0x8: # an external method + sym = MethodDecl(name, nargs, tree) + else: + sym = NamedDecl(name, tree) + context.register_symbol(sym) + +def DefField_hook_post(context, tree): + # Update the fields with region & offset info + region_name = context.lookup_symbol(tree.children[1].children).name + fields = tree.children[3].children + bit_offset = 0 + for field in fields: + field = field.children[0] + if field.label == "NamedField": + name = field.children[0].children + length = field.children[1].children + sym = context.lookup_symbol(name) + assert isinstance(sym, OperationFieldDecl) + sym.set_location(region_name, bit_offset) + bit_offset += length + elif field.label == "ReservedField": + length = field.children[0].children + bit_offset += length + else: + break + +def NamedField_hook_post(context, tree): + name = tree.children[0].children + length = tree.children[1].children + sym = OperationFieldDecl(name, length, tree) + context.register_symbol(sym) + +def DefMethod_hook_named(context, tree, name): + context.change_scope(name) + +def DefMethod_hook_post(context, tree): + context.pop_scope() + if len(tree.children) >= 3: + name = tree.children[1].children + flags = tree.children[2].children[0].children + nargs = flags & 0x7 + sym = MethodDecl(name, nargs, tree) + context.register_symbol(sym) + +def DefOpRegion_hook_named(context, tree, name): + sym = NamedDecl(name, tree) + context.register_symbol(sym) + +def DefPowerRes_hook_named(context, tree, name): + sym = NamedDecl(name, tree) + context.register_symbol(sym) + +def DefThermalZone_hook_named(context, tree, name): + sym = NamedDecl(name, tree) + context.register_symbol(sym) + +################################################################################ +# Instantiate parsers +################################################################################ + +def register_hooks(factory, label): + if f"{sym}_hook_pre" in globals().keys(): + factory.hook_pre = globals()[f"{sym}_hook_pre"] + if f"{sym}_hook_named" in globals().keys(): + factory.hook_named = globals()[f"{sym}_hook_named"] + if f"{sym}_hook_post" in globals().keys(): + factory.hook_post = globals()[f"{sym}_hook_post"] + +for sym in dir(grammar): + # Ignore builtin members and opcode constants + if sym.startswith("__") or (sym.upper() == sym): + continue + + definition = getattr(grammar, sym) + if isinstance(definition, tuple): + factory = SequenceFactory(sym, definition) + register_hooks(factory, sym) + globals()[sym] = factory + elif isinstance(definition, list): + factory = OptionFactory(sym, definition) + register_hooks(factory, sym) + globals()[sym] = factory diff --git a/misc/config_tools/board_inspector/acpiparser/aml/stream.py b/misc/config_tools/board_inspector/acpiparser/aml/stream.py new file mode 100644 index 000000000..391eb1100 --- /dev/null +++ b/misc/config_tools/board_inspector/acpiparser/aml/stream.py @@ -0,0 +1,113 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +from .exception import * +from .grammar import AML_EXT_OP_PREFIX + +class Stream: + def print_binary(self, base): + acc = f"[{hex(base)}/{hex(len(self.data))}]" + converted = "" + for i in range(base, base + 16): + code = ord(self.data[i]) + acc += " %02x" % code + if code >= 0x20 and code <= 0x7E: + converted += chr(code) + else: + converted += "." + if i > base and ((i - base) % 4) == 3: + acc += " " + converted += " " + acc += f" '{converted}'" + print(acc) + + def __init__(self, data): + self.data = data + self.current = 0 + self.scopes = [len(data)] + + def peek_integer(self, count): + if self.current + count > self.scopes[-1]: + raise ScopeMismatch + ret = 0 + for i in range(0, count): + ret += (self.data[self.current + i] << (i * 8)) + return ret + + def get_integer(self, count): + if self.current + count > self.scopes[-1]: + raise ScopeMismatch + ret = self.peek_integer(count) + self.current += count + return ret + + def get_char(self): + if self.current + 1 > self.scopes[-1]: + raise ScopeMismatch + ret = chr(self.data[self.current]) + self.current += 1 + return ret + + def peek_opcode(self): + opcode = self.peek_integer(1) + if opcode == AML_EXT_OP_PREFIX: + opcode = self.peek_integer(2) + return (opcode, 2) + return (opcode, 1) + + def get_opcode(self): + opcode = self.get_integer(1) + if opcode == AML_EXT_OP_PREFIX: + opcode += (self.get_integer(1) << 8) + return (opcode, 2) + return (opcode, 1) + + def get_fixed_length_string(self, count): + if self.current + count > self.scopes[-1]: + raise ScopeMismatch + ret = self.data[self.current : self.current + count].decode("latin-1") + self.current += count + return ret + + def get_string(self): + null = self.data.find(0x00, self.current) + assert null >= 0 + if null + 1 > self.scopes[-1]: + raise ScopeMismatch + ret = self.data[self.current:null].decode("latin-1") + self.current = null + 1 + return ret + + def get_buffer(self): + cur = self.current + self.current = self.scopes[-1] + return self.data[cur:self.current] + + def seek(self, offset, absolute=False): + if absolute: + self.current = offset + else: + self.current += offset + + def at_end(self): + return self.current == self.scopes[-1] + + def push_scope(self, size): + self.scopes.append(self.current + size) + + def pop_scope(self, force=False): + if not force and not self.at_end(): + raise ScopeMismatch + self.scopes.pop() + + def reset(self): + self.current = 0 + self.scopes = [len(self.data)] + + def dump(self): + self.print_binary(self.current - 48) + self.print_binary(self.current - 32) + self.print_binary(self.current - 16) + self.print_binary(self.current) diff --git a/misc/config_tools/board_inspector/acpiparser/aml/tree.py b/misc/config_tools/board_inspector/acpiparser/aml/tree.py new file mode 100644 index 000000000..ae2ecbcb8 --- /dev/null +++ b/misc/config_tools/board_inspector/acpiparser/aml/tree.py @@ -0,0 +1,111 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +from copy import copy + +from . import grammar + +class Tree: + def __init__(self, label=None, children=[]): + self.label = label + self.children = copy(children) + self.scope = None + + self.package_range = None + + self.deferred_range = None + self.context_scope = None + self.factory = None + + def append_child(self, child): + self.children.append(child) + +class Visitor: + def __init__(self): + self.depth = 0 + + def __visit(self, tree): + fn = getattr(self, tree.label, None) + if not fn: + fn = getattr(self, "default", None) + if fn: + fn(tree) + + def visit_topdown(self, tree): + self.__visit(tree) + if isinstance(tree.children, list): + self.depth += 1 + for child in tree.children: + if isinstance(child, Tree): + self.visit_topdown(child) + self.depth -= 1 + +class Transformer: + def __init__(self): + self.depth = 0 + + def __transform(self, tree): + fn = getattr(self, tree.label, None) + if not fn: + fn = getattr(self, "default", None) + if fn: + return fn(tree) + else: + return tree + + def transform_topdown(self, tree): + new_tree = self.__transform(tree) + if isinstance(new_tree.children, list): + self.depth += 1 + for i, child in enumerate(tree.children): + if isinstance(child, Tree): + tree.children[i] = self.transform_topdown(child) + self.depth -= 1 + return new_tree + + def transform_bottomup(self, tree): + if isinstance(tree.children, list): + self.depth += 1 + for i, child in enumerate(tree.children): + if isinstance(child, Tree): + tree.children[i] = self.transform_bottomup(child) + self.depth -= 1 + return self.__transform(tree) + +single_operand_op = ["MethodInvocation"] +for sym in dir(grammar): + # Ignore builtin members and opcode constants + if sym.startswith("__") or (sym.upper() == sym): + continue + + definition = getattr(grammar, sym) + if isinstance(definition, tuple) and\ + isinstance(definition[0], int) and\ + len(definition) == 2: + single_operand_op.append(sym) + +class FlattenTransformer(Transformer): + def default(self, tree): + if tree.label not in single_operand_op and \ + isinstance(tree.children, list) and \ + len(tree.children) == 1 and \ + not tree.deferred_range: + return tree.children[0] + else: + return tree + +class Interpreter: + def __init__(self, context): + self.context = context + + def interpret(self, expr): + assert isinstance(expr, Tree) + fn = getattr(self, expr.label, None) + if not fn: + fn = getattr(self, "default", None) + if fn: + return fn(expr) + else: + raise NotImplementedError(f"don't know how to interpret a tree node with label {expr.label}") diff --git a/misc/config_tools/board_inspector/acpiparser/aml/visitors.py b/misc/config_tools/board_inspector/acpiparser/aml/visitors.py new file mode 100644 index 000000000..b85476f29 --- /dev/null +++ b/misc/config_tools/board_inspector/acpiparser/aml/visitors.py @@ -0,0 +1,68 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +from .tree import Visitor + +class PrintLayoutVisitor(Visitor): + @staticmethod + def __is_printable(s): + return all(ord(c) >= 0x20 and ord(c) < 0xFF for c in s) + + def default(self, tree): + indent = " " * self.depth + print(f"{indent}{tree.label}", end="") + if isinstance(tree.children, int): + print(f" = {hex(tree.children)}", end="") + elif isinstance(tree.children, str): + if self.__is_printable(tree.children): + print(f" = '{tree.children}'", end="") + if tree.deferred_range: + print(f" (deferred at {hex(tree.deferred_range[0])}, length {hex(tree.deferred_range[1])})", end="") + if tree.factory: + print(f" {tree.factory.label}: {tree.factory.seq}", end="") + print() + +class ConditionallyUnregisterSymbolVisitor(Visitor): + def __init__(self, interpreter): + super().__init__() + self.context = interpreter.context + self.interpreter = interpreter + self.conditionally_hidden = False + + self.DefName = self.__unregister(0) + self.DefMethod = self.__unregister(1) + self.DefDevice = self.__unregister(1) + + def __unregister(self, name_string_idx): + def f(tree): + if self.conditionally_hidden: + scope = tree.scope + name = tree.children[name_string_idx].children + realpath = self.context.realpath(scope, name) + self.context.unregister_object(realpath) + return f + + def visit_topdown(self, tree): + if tree.label == "DefIfElse": + self.context.change_scope(tree.scope) + cond = self.interpreter.interpret(tree.children[1]).get() + self.context.pop_scope() + + self.depth += 1 + if cond: + self.visit_topdown(tree.children[2]) + if len(tree.children) == 4: + self.conditionally_hidden = True + self.visit_topdown(tree.children[3]) + self.conditionally_hidden = False + else: + self.conditionally_hidden = True + self.visit_topdown(tree.children[2]) + self.conditionally_hidden = False + if len(tree.children) == 4: + self.visit_topdown(tree.children[3]) + self.depth -= 1 + elif tree.label not in ["DefMethod"]: + super().visit_topdown(tree) diff --git a/misc/config_tools/board_inspector/acpiparser/dsdt.py b/misc/config_tools/board_inspector/acpiparser/dsdt.py new file mode 100644 index 000000000..4af22c106 --- /dev/null +++ b/misc/config_tools/board_inspector/acpiparser/dsdt.py @@ -0,0 +1,42 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import os +import logging + +from acpiparser.aml.stream import Stream +from acpiparser.aml.parser import AMLCode, DeferredExpansion +from acpiparser.aml.tree import Tree, FlattenTransformer +from acpiparser.aml.context import Context +from acpiparser.aml.interpreter import ConcreteInterpreter +from acpiparser.aml.visitors import ConditionallyUnregisterSymbolVisitor + +def DSDT(val): + table_dir = os.path.dirname(val) + if not table_dir: + table_dir = "." + ssdt = filter(lambda x: x.startswith("SSDT"), os.listdir(table_dir)) + tables = [val] + list(map(lambda x: os.path.join(table_dir, x), ssdt)) + + context = Context() + trees = [] + try: + for t in tables: + logging.info(f"Loading {t}") + context.switch_stream(t) + tree = Tree() + AMLCode.parse(context, tree) + tree = DeferredExpansion(context).transform_topdown(tree) + tree = FlattenTransformer().transform_bottomup(tree) + trees.append(tree) + except Exception as e: + context.current_stream.dump() + raise + + context.skip_external_on_lookup() + visitor = ConditionallyUnregisterSymbolVisitor(ConcreteInterpreter(context)) + for tree in trees: + visitor.visit_topdown(tree) + return context diff --git a/misc/config_tools/board_inspector/acpiparser/rdt.py b/misc/config_tools/board_inspector/acpiparser/rdt.py new file mode 100644 index 000000000..4412cc4a4 --- /dev/null +++ b/misc/config_tools/board_inspector/acpiparser/rdt.py @@ -0,0 +1,622 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import ctypes + +import lib.cdata as cdata +import lib.unpack as unpack + +# 6.4.2 Small Resource Data Type + +class SmallResourceDataTag(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('length', ctypes.c_uint8, 3), + ('name', ctypes.c_uint8, 4), + ('type', ctypes.c_uint8, 1), + ] + +SMALL_RESOURCE_ITEM_IRQ_FORMAT = 0x04 +SMALL_RESOURCE_ITEM_DMA_FORMAT = 0x05 +SMALL_RESOURCE_ITEM_START_DEPENDENT_FUNCTIONS = 0x06 +SMALL_RESOURCE_ITEM_END_DEPENDENT_FUNCTIONS = 0x07 +SMALL_RESOURCE_ITEM_IO_PORT = 0x08 +SMALL_RESOURCE_ITEM_FIXED_LOCATION_IO_PORT = 0x09 +SMALL_RESOURCE_ITEM_FIXED_DMA = 0x0A +SMALL_RESOURCE_ITEM_VENDOR_DEFINED = 0x0E +SMALL_RESOURCE_ITEM_END_TAG = 0x0F + +# 6.4.2.1 IRQ Descriptor + +def SmallResourceItemIRQ_factory(_len): + class SmallResourceItemIRQ(cdata.Struct): + _pack_ = 1 + _fields_ = SmallResourceDataTag._fields_ + [ + ('_INT', ctypes.c_uint16), + ] + ([ + ('_HE', ctypes.c_uint8, 1), + ('ingored', ctypes.c_uint8, 2), + ('_LL', ctypes.c_uint8, 1), + ('_SHR', ctypes.c_uint8, 1), + ('_WKC', ctypes.c_uint8, 1), + ('reserved', ctypes.c_uint8, 2), + ] if (_len > 2) else []) + return SmallResourceItemIRQ + +# 6.4.2.2 DMA Descriptor + +class SmallResourceItemDMA(cdata.Struct): + _pack_ = 1 + _fields_ = SmallResourceDataTag._fields_ + [ + ('_DMA', ctypes.c_uint8), + ('_SIZ', ctypes.c_uint8, 2), + ('_BM', ctypes.c_uint8, 1), + ('ignored', ctypes.c_uint8, 2), + ('_TYP', ctypes.c_uint8, 2), + ('reserved', ctypes.c_uint8, 1), + ] + +# 6.4.2.3 Start Dependent Functions Descriptor + +def SmallResourceItemStartDependentFunctions_factory(_len): + class SmallResourceItemStartDependentFunctions(cdata.Struct): + _pack_ = 1 + _fields_ = SmallResourceDataTag._fields_ + ([ + ('compatibility', ctypes.c_uint8, 2), + ('performance', ctypes.c_uint8, 2), + ('reserved', ctypes.c_uint8, 4), + ] if (_len > 0) else []) + return SmallResourceItemStartDependentFunctions + +# 6.4.2.4 End Dependent Functions Descriptor + +class SmallResourceItemEndDependentFunctions(cdata.Struct): + _pack_ = 1 + _fields_ = SmallResourceDataTag._fields_ + +# 6.4.2.5 I/O Port Descriptor + +io_port_decoding = { + 0b0: 'Decodes bits[9:0]', + 0b1: 'Decodes bits[15:0]', +} + +class SmallResourceItemIOPort(cdata.Struct): + _pack_ = 1 + _fields_ = SmallResourceDataTag._fields_ + [ + ('_DEC', ctypes.c_uint8, 1), + ('reserved', ctypes.c_uint8, 7), + ('_MIN', ctypes.c_uint16), + ('_MAX', ctypes.c_uint16), + ('_ALN', ctypes.c_uint8), + ('_LEN', ctypes.c_uint8), + ] + _formats = { + '_DEC': unpack.format_table("{}", io_port_decoding) + } + +# 6.4.2.6 Fixed Location I/O Port Descriptor + +class SmallResourceItemFixedLocationIOPort(cdata.Struct): + _pack_ = 1 + _fields_ = SmallResourceDataTag._fields_ + [ + ('_BAS', ctypes.c_uint16), + ('_LEN', ctypes.c_uint8), + ] + +# 6.4.2.7 Fixed DMA Descriptor + +class SmallResourceItemFixedDMA(cdata.Struct): + _pack_ = 1 + _fields_ = SmallResourceDataTag._fields_ + [ + ('_DMA', ctypes.c_uint16), + ('_TYP', ctypes.c_uint16), + ('_SIZ', ctypes.c_uint8), + ] + +# 6.4.2.8 Vendor-Defined Descriptor, Type 0 + +def SmallResourceItemVendorDefined_factory(_len): + class SmallResourceItemVendorDefined(cdata.Struct): + _pack_ = 1 + _fields_ = SmallResourceDataTag._fields_ + [ + ('data', ctypes.c_uint8 * _len), + ] + return SmallResourceItemVendorDefined + +# 6.4.2.9 End Tag + +class SmallResourceItemEndTag(cdata.Struct): + _pack_ = 1 + _fields_ = SmallResourceDataTag._fields_ + [ + ('checksum', ctypes.c_uint8) + ] + +# 6.4.3 Large Resource Data Type + +class LargeResourceDataTag(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('name', ctypes.c_uint8, 7), + ('type', ctypes.c_uint8, 1), + ('length', ctypes.c_uint16), + ] + +LARGE_RESOURCE_ITEM_24BIT_MEMORY_RANGE = 0x01 +LARGE_RESOURCE_ITEM_GENERIC_REGISTER = 0x02 +LARGE_RESOURCE_ITEM_VENDOR_DEFINED = 0x04 +LARGE_RESOURCE_ITEM_32BIT_MEMORY_RANGE = 0x05 +LARGE_RESOURCE_ITEM_32BIT_FIXED_MEMORY_RANGE = 0x06 +LARGE_RESOURCE_ITEM_ADDRESS_SPACE_RESOURCE = 0x07 +LARGE_RESOURCE_ITEM_WORD_ADDRESS_SPACE = 0x08 +LARGE_RESOURCE_ITEM_EXTENDED_INTERRUPT = 0x09 +LARGE_RESOURCE_ITEM_QWORD_ADDRESS_SPACE = 0x0A +LARGE_RESOURCE_ITEM_EXTENDED_ADDRESS_SPACE = 0x0B +LARGE_RESOURCE_ITEM_GPIO_CONNECTION = 0x0C +LARGE_RESOURCE_ITEM_PIN_FUNCTION = 0x0D +LARGE_RESOURCE_ITEM_GENERIC_SERIAL_BUS_CONNECTION = 0x0E +LARGE_RESOURCE_ITEM_PIN_CONFIGURATION = 0x0F +LARGE_RESOURCE_ITEM_PIN_GROUP = 0x10 +LARGE_RESOURCE_ITEM_PIN_GROUP_FUNCTION = 0x11 +LARGE_RESOURCE_ITEM_PIN_GROUP_CONFIGURATION = 0x12 + +# 6.4.3.1 24-Bit Memory Range Descriptor + +class LargeResourceItem24BitMemoryRange(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_RW', ctypes.c_uint8, 1), + ('ignored', ctypes.c_uint8, 7), + ('_MIN', ctypes.c_uint16), + ('_MAX', ctypes.c_uint16), + ('_ALN', ctypes.c_uint16), + ('_LEN', ctypes.c_uint16), + ] + +# 6.4.3.2 Vendor-Defined Descriptor, Type 1 + +def LargeResourceItemVendorDefined_factory(_len): + class LargeResourceItemVendorDefined(cdata.Struct): + _pack_ = 1 + _fields_ = SmallResourceDataTag._fields_ + [ + ('subtype', ctypes.c_uint8), + ('UUID', ctypes.c_uint8 * 16), + ('data', ctypes.c_uint8 * (_len - 17)), + ] + return LargeResourceItemVendorDefined + +# 6.4.3.3 32-Bit Memory Range Descriptor + +class LargeResourceItem32BitMemoryRange(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_RW', ctypes.c_uint8, 1), + ('ignored', ctypes.c_uint8, 7), + ('_MIN', ctypes.c_uint32), + ('_MAX', ctypes.c_uint32), + ('_ALN', ctypes.c_uint32), + ('_LEN', ctypes.c_uint32), + ] + +# 6.4.3.4 32-Bit Fixed Memory Range Descriptor + +class LargeResourceItem32BitFixedMemoryRange(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_RW', ctypes.c_uint8, 1), + ('ignored', ctypes.c_uint8, 7), + ('_BAS', ctypes.c_uint32), + ('_LEN', ctypes.c_uint32), + ] + +# 6.4.3.5 Address Space Resource Descriptors + +resource_type = { + 0x00: 'Memory range', + 0x01: 'I/O range', + 0x02: 'Bus number', +} + +decode_type = { + 0b0: 'This bridge positively decodes this address', + 0b1: 'This bridge subtractively decodes this address', +} + +min_address_fixed = { + 0b0: 'The specified minimum address is not fixed', + 0b1: 'The specified minimum address is fixed', +} + +max_address_fixed = { + 0b0: 'The specified maximum address is not fixed', + 0b1: 'The specified maximum address is fixed', +} + +# 6.4.3.5.1 QWord Address Space Descriptor + +def LargeResourceItemQWordAddressSpace_factory(_len): + class LargeResourceItemQWordAddressSpace(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_TYP', ctypes.c_uint8), + ('ignored', ctypes.c_uint8, 1), + ('_DEC', ctypes.c_uint8, 1), + ('_MIF', ctypes.c_uint8, 1), + ('_MAF', ctypes.c_uint8, 1), + ('reserved1', ctypes.c_uint8, 4), + ('flags', ctypes.c_uint8), + ('_GRA', ctypes.c_uint64), + ('_MIN', ctypes.c_uint64), + ('_MAX', ctypes.c_uint64), + ('_TRA', ctypes.c_uint64), + ('_LEN', ctypes.c_uint64), + ] + ([ + ('reserved2', ctypes.c_uint8) + ] if (_len > 43) else []) + ([ + ('source', ctypes.c_char * (_len - 44)) + ] if (_len > 44) else []) + _formats = { + '_TYP': unpack.format_table("{}", resource_type), + '_DEC': unpack.format_table("{}", decode_type), + '_MIF': unpack.format_table("{}", min_address_fixed), + '_MAF': unpack.format_table("{}", max_address_fixed), + } + return LargeResourceItemQWordAddressSpace + +# 6.4.3.5.2 DWord Address Space Descriptor + +def LargeResourceItemDWordAddressSpace_factory(_len): + class LargeResourceItemDWordAddressSpace(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_TYP', ctypes.c_uint8), + ('ignored', ctypes.c_uint8, 1), + ('_DEC', ctypes.c_uint8, 1), + ('_MIF', ctypes.c_uint8, 1), + ('_MAF', ctypes.c_uint8, 1), + ('reserved1', ctypes.c_uint8, 4), + ('flags', ctypes.c_uint8), + ('_GRA', ctypes.c_uint32), + ('_MIN', ctypes.c_uint32), + ('_MAX', ctypes.c_uint32), + ('_TRA', ctypes.c_uint32), + ('_LEN', ctypes.c_uint32), + ] + ([ + ('reserved2', ctypes.c_uint8) + ] if (_len > 23) else []) + ([ + ('source', ctypes.c_char * (_len - 24)) + ] if (_len > 24) else []) + _formats = { + '_TYP': unpack.format_table("{}", resource_type), + '_DEC': unpack.format_table("{}", decode_type), + '_MIF': unpack.format_table("{}", min_address_fixed), + '_MAF': unpack.format_table("{}", max_address_fixed), + } + return LargeResourceItemDWordAddressSpace + +# 6.4.3.5.3 Word Address Space Descriptor + +def LargeResourceItemWordAddressSpace_factory(_len): + class LargeResourceItemWordAddressSpace(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_TYP', ctypes.c_uint8), + ('ignored', ctypes.c_uint8, 1), + ('_DEC', ctypes.c_uint8, 1), + ('_MIF', ctypes.c_uint8, 1), + ('_MAF', ctypes.c_uint8, 1), + ('reserved1', ctypes.c_uint8, 4), + ('flags', ctypes.c_uint8), + ('_GRA', ctypes.c_uint16), + ('_MIN', ctypes.c_uint16), + ('_MAX', ctypes.c_uint16), + ('_TRA', ctypes.c_uint16), + ('_LEN', ctypes.c_uint16), + ] + ([ + ('reserved2', ctypes.c_uint8) + ] if (_len > 13) else []) + ([ + ('source', ctypes.c_char * (_len - 14)) + ] if (_len > 14) else []) + _formats = { + '_TYP': unpack.format_table("{}", resource_type), + '_DEC': unpack.format_table("{}", decode_type), + '_MIF': unpack.format_table("{}", min_address_fixed), + '_MAF': unpack.format_table("{}", max_address_fixed), + } + return LargeResourceItemWordAddressSpace + +# 6.4.3.5.4 Extended Address Space Descriptor + +class LargeResourceItemExtendedAddressSpace(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_TYP', ctypes.c_uint8), + ('ignored', ctypes.c_uint8, 1), + ('_DEC', ctypes.c_uint8, 1), + ('_MIF', ctypes.c_uint8, 1), + ('_MAF', ctypes.c_uint8, 1), + ('reserved1', ctypes.c_uint8, 4), + ('flags', ctypes.c_uint8), + ('revision', ctypes.c_uint8), + ('reserved2', ctypes.c_uint8), + ('_GRA', ctypes.c_uint64), + ('_MIN', ctypes.c_uint64), + ('_MAX', ctypes.c_uint64), + ('_TRA', ctypes.c_uint64), + ('_LEN', ctypes.c_uint64), + ('_ATT', ctypes.c_uint64), + ] + _formats = { + '_TYP': unpack.format_table("{}", resource_type), + '_DEC': unpack.format_table("{}", decode_type), + '_MIF': unpack.format_table("{}", min_address_fixed), + '_MAF': unpack.format_table("{}", max_address_fixed), + } + +# 6.4.3.6 Extended Interrupt Descriptor + +class LargeResourceItemExtendedInterruptHeader(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('flags', ctypes.c_uint8), + ('_LEN', ctypes.c_uint8), + ] + +def LargeResourceItemExtendedInterrupt_factory(_len, _it_len): + class LargeResourceItemExtendedInterrupt(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_CP', ctypes.c_uint8, 1), + ('_HE', ctypes.c_uint8, 1), + ('_LL', ctypes.c_uint8, 1), + ('_SHR', ctypes.c_uint8, 1), + ('_WKC', ctypes.c_uint8, 1), + ('reserved1', ctypes.c_uint8, 3), + ('_LEN', ctypes.c_uint8), + ('_INT', ctypes.c_uint32 * _it_len), + ] + ([ + ('reserved2', ctypes.c_uint8), + ] if (_len > 2 + _it_len * 4) else []) + ([ + ('source', ctypes.c_char * (_len - _it_len * 4 - 3)) + ] if (_len > 2 + _it_len * 4 + 1) else []) + return LargeResourceItemExtendedInterrupt + +# 6.4.3.7 Generic Register Descriptor + +class LargeResourceItemGenericRegister(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_ASI', ctypes.c_uint8), + ('_RBW', ctypes.c_uint8), + ('_RBO', ctypes.c_uint8), + ('_ASZ', ctypes.c_uint8), + ('_ADR', ctypes.c_uint64), + ] + +# 6.4.3.8.1 GPIO Connection Descriptor (TODO) + +def LargeResourceItemGPIOConnection_factory(_len): + class LargeResourceItemGPIOConnection(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_REV', ctypes.c_uint8), + ('_TYP', ctypes.c_uint8), + ('data', ctypes.c_uint8 * (_len - 2)), + ] + return LargeResourceItemGPIOConnection + +# 6.4.3.8.2 GenericSerialBus Connection Descriptors (TODO) + +def LargeResourceItemGenericSerialBusConnection_factory(_len): + class LargeResourceItemGenericSerialBusConnection(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_REV', ctypes.c_uint8), + ('_RSI', ctypes.c_uint8), + ('_TYP', ctypes.c_uint8), + ('data', ctypes.c_uint8 * (_len - 3)), + ] + return LargeResourceItemGenericSerialBusConnection + +# 6.4.3.9 Pin Function Descriptor + +def LargeResourceItemPinFunction_factory(_len): + class LargeResourceItemPinFunction(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_REV', ctypes.c_uint8), + ('_SHR', ctypes.c_uint8, 1), + ('reserved1', ctypes.c_uint16, 15), + ('_PPC', ctypes.c_uint8), + ('_FUN', ctypes.c_uint16), + ('_PTO', ctypes.c_uint16), + ('reserved2', ctypes.c_uint8), + ('_RNI', ctypes.c_uint16), + ('_VDO', ctypes.c_uint16), + ('_VDL', ctypes.c_uint16), + ('data', ctypes.c_uint8 * (_len - 18)), + ] + return LargeResourceItemPinFunction + +# 6.4.3.10 Pin Configuration Descriptor + +def LargeResourceItemPinConfiguration_factory(_len): + class LargeResourceItemPinConfiguration(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_REV', ctypes.c_uint8), + ('_SHR', ctypes.c_uint8, 1), + ('_CP', ctypes.c_uint8, 1), + ('reserved1', ctypes.c_uint16, 14), + ('_TYP', ctypes.c_uint8), + ('_VAL', ctypes.c_uint32), + ('_PTO', ctypes.c_uint16), + ('reserved2', ctypes.c_uint8), + ('_RNO', ctypes.c_uint16), + ('_VDO', ctypes.c_uint16), + ('_VDL', ctypes.c_uint16), + ('data', ctypes.c_uint8 * (_len - 20)), + ] + return LargeResourceItemPinConfiguration + +# 6.4.3.11 Pin Group Descriptor + +def LargeResourceItemPinGroup_factory(_len): + class LargeResourceItemPinGroup(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_REV', ctypes.c_uint8), + ('_CP', ctypes.c_uint8, 1), + ('reserved1', ctypes.c_uint16, 15), + ('_PTO', ctypes.c_uint16), + ('_RLO', ctypes.c_uint16), + ('_VDO', ctypes.c_uint16), + ('_VDL', ctypes.c_uint16), + ('data', ctypes.c_uint8 * (_len - 14)), + ] + return LargeResourceItemPinGroup + +# 6.4.3.12 Pin Group Function Descriptor + +def LargeResourceItemPinGroupFunction_factory(_len): + class LargeResourceItemPinGroupFunction(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_REV', ctypes.c_uint8), + ('_SHR', ctypes.c_uint8, 1), + ('_CP', ctypes.c_uint8, 1), + ('reserved1', ctypes.c_uint16, 14), + ('_FUN', ctypes.c_uint16), + ('reserved2', ctypes.c_uint8), + ('_RNI', ctypes.c_uint16), + ('_RLO', ctypes.c_uint16), + ('_VDO', ctypes.c_uint16), + ('_VDL', ctypes.c_uint16), + ('data', ctypes.c_uint8 * (_len - 17)), + ] + return LargeResourceItemPinGroupFunction + +# 6.4.3.13 Pin Group Configuration Descriptor + +def LargeResourceItemPinGroupConfiguration_factory(_len): + class LargeResourceItemPinGroupConfiguration(cdata.Struct): + _pack_ = 1 + _fields_ = LargeResourceDataTag._fields_ + [ + ('_REV', ctypes.c_uint8), + ('_SHR', ctypes.c_uint8, 1), + ('_CP', ctypes.c_uint8, 1), + ('reserved1', ctypes.c_uint16, 14), + ('_TYP', ctypes.c_uint8), + ('_VAL', ctypes.c_uint32), + ('reserved2', ctypes.c_uint8), + ('_RNO', ctypes.c_uint16), + ('_RLO', ctypes.c_uint16), + ('_VDO', ctypes.c_uint16), + ('_VDL', ctypes.c_uint16), + ('data', ctypes.c_uint8 * (_len - 20)), + ] + return LargeResourceItemPinGroupConfiguration + +# The parser + +def rdt_item_list(addr, length): + end = addr + length + field_list = list() + item_num = 0 + while addr < end: + item_num += 1 + tag = SmallResourceDataTag.from_address(addr) + if tag.type == 0: # Small type + if tag.name == SMALL_RESOURCE_ITEM_IRQ_FORMAT: + cls = SmallResourceItemIRQ_factory(tag.length) + elif tag.name == SMALL_RESOURCE_ITEM_DMA_FORMAT: + cls = SmallResourceItemDMA + elif tag.name == SMALL_RESOURCE_ITEM_START_DEPENDENT_FUNCTIONS: + cls = SmallResourceItemStartDependentFunctions_factory(tag.length) + elif tag.name == SMALL_RESOURCE_ITEM_END_DEPENDENT_FUNCTIONS: + cls = SmallResourceItemEndDependentFunctions + elif tag.name == SMALL_RESOURCE_ITEM_IO_PORT: + cls = SmallResourceItemIOPort + elif tag.name == SMALL_RESOURCE_ITEM_FIXED_LOCATION_IO_PORT: + cls = SmallResourceItemFixedLocationIOPort + elif tag.name == SMALL_RESOURCE_ITEM_FIXED_DMA: + cls = SmallResourceItemFixedDMA + elif tag.name == SMALL_RESOURCE_ITEM_VENDOR_DEFINED: + cls = SmallResourceItemVendorDefined_factory(tag.length) + elif tag.name == SMALL_RESOURCE_ITEM_END_TAG: + cls = SmallResourceItemEndTag + else: + raise NotImplementedError(f"Unknown small resource item name: {tag.name}, offset {addr}") + size = ctypes.sizeof(cls) + assert size == tag.length + 1, f"{cls}: {size} != {tag.length} + 1" + addr += size + else: # Large type + tag = LargeResourceDataTag.from_address(addr) + if tag.name == LARGE_RESOURCE_ITEM_24BIT_MEMORY_RANGE: + cls = LargeResourceItem24BitMemoryRange + elif tag.name == LARGE_RESOURCE_ITEM_GENERIC_REGISTER: + cls = LargeResourceItemGenericRegister + elif tag.name == LARGE_RESOURCE_ITEM_VENDOR_DEFINED: + cls = LargeResourceItemVendorDefined_factory(tag.length) + elif tag.name == LARGE_RESOURCE_ITEM_32BIT_MEMORY_RANGE: + cls = LargeResourceItem32BitMemoryRange + elif tag.name == LARGE_RESOURCE_ITEM_32BIT_FIXED_MEMORY_RANGE: + cls = LargeResourceItem32BitFixedMemoryRange + elif tag.name == LARGE_RESOURCE_ITEM_ADDRESS_SPACE_RESOURCE: + cls = LargeResourceItemDWordAddressSpace_factory(tag.length) + elif tag.name == LARGE_RESOURCE_ITEM_WORD_ADDRESS_SPACE: + cls = LargeResourceItemWordAddressSpace_factory(tag.length) + elif tag.name == LARGE_RESOURCE_ITEM_EXTENDED_INTERRUPT: + hdr = LargeResourceItemExtendedInterruptHeader.from_address(addr) + cls = LargeResourceItemExtendedInterrupt_factory(tag.length, hdr._LEN) + elif tag.name == LARGE_RESOURCE_ITEM_QWORD_ADDRESS_SPACE: + cls = LargeResourceItemQWordAddressSpace_factory(tag.length) + elif tag.name == LARGE_RESOURCE_ITEM_EXTENDED_ADDRESS_SPACE: + cls = LargeResourceItemExtendedAddressSpace + elif tag.name == LARGE_RESOURCE_ITEM_GPIO_CONNECTION: + cls = LargeResourceItemGPIOConnection_factory(tag.length) + elif tag.name == LARGE_RESOURCE_ITEM_PIN_FUNCTION: + cls = LargeResourceItemPinFunction_factory(tag.length) + elif tag.name == LARGE_RESOURCE_ITEM_GENERIC_SERIAL_BUS_CONNECTION: + cls = LargeResourceItemGenericSerialBusConnection_factory(tag.length) + elif tag.name == LARGE_RESOURCE_ITEM_PIN_CONFIGURATION: + cls = LargeResourceItemPinConfiguration_factory(tag.length) + elif tag.name == LARGE_RESOURCE_ITEM_PIN_GROUP: + cls = LargeResourceItemPinGroup_factory(tag.length) + elif tag.name == LARGE_RESOURCE_ITEM_PIN_GROUP_FUNCTION: + cls = LargeResourceItemPinGroupFunction_factory(tag.length) + elif tag.name == LARGE_RESOURCE_ITEM_PIN_GROUP_CONFIGURATION: + cls = LargeResourceItemPinGroupConfiguration_factory(tag.length) + else: + raise NotImplementedError(f"Unknown Large resource item name: {tag.name}, offset {addr}") + size = ctypes.sizeof(cls) + assert size == tag.length + 3, f"{cls}: {size} != {tag.length} + 3" + addr += size + field_list.append((f'item{item_num}', cls)) + return field_list + +def rdt_factory(field_list): + + class items(cdata.Struct): + _pack_ = 1 + _fields_ = field_list + + def __iter__(self): + for f in self._fields_: + yield getattr(self, f[0]) + + class ResourceData(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('items', items) + ] + return ResourceData + +def parse_resource_data(data): + """Parse ACPI resource data types returned by _CRS, _PRS and _SRS control methods.""" + buf = ctypes.create_string_buffer(bytes(data), len(data)) + addr = ctypes.addressof(buf) + item_list = rdt_item_list(addr, len(data)) + return rdt_factory(item_list).from_buffer_copy(data) diff --git a/misc/config_tools/board_inspector/lib/mmio.py b/misc/config_tools/board_inspector/lib/mmio.py new file mode 100644 index 000000000..eecdd6810 --- /dev/null +++ b/misc/config_tools/board_inspector/lib/mmio.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +import argparse +import lxml.etree + +def mmio_regions(etree): + ret = [] + + resources = etree.xpath("//resources/mmio") + for res in resources: + base = res.get("min") + top = res.get("max") + dev = res.getparent().getparent() + obj = dev.get("object") + ret.append((obj, int(base, base=16), int(top, base=16))) + + return sorted(ret, key=lambda x:(x[1], x[2])) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("file", help="board XML file") + args = parser.parse_args() + + etree = lxml.etree.parse(args.file) + regions = mmio_regions(etree) + for region in regions: + print("%-4s 0x%08x 0x%08x" % (region[0], region[1], region[2]))