board_inspector/acpiparser: add DSDT/SSDT parser

This patch adds a parser and interpreter of ACPI DSDT/SSDT tables in
AML (ACPI Machine Language) in order to understand the complete device
layout and resource allocation.

Kindly note that the interpreter is still experimental and not yet
complete.

Tracked-On: #5922
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
Junjie Mao 2021-02-07 16:46:31 +08:00 committed by wenlingz
parent 6276e5759a
commit 0215603812
12 changed files with 3287 additions and 0 deletions

View File

@ -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)")

View File

@ -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

View File

@ -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

View File

@ -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,)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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}")

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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]))