mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-29 08:47:24 +00:00
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:
parent
6276e5759a
commit
0215603812
334
misc/config_tools/board_inspector/acpiparser/aml/context.py
Normal file
334
misc/config_tools/board_inspector/acpiparser/aml/context.py
Normal 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)")
|
327
misc/config_tools/board_inspector/acpiparser/aml/datatypes.py
Normal file
327
misc/config_tools/board_inspector/acpiparser/aml/datatypes.py
Normal 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
|
@ -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
|
494
misc/config_tools/board_inspector/acpiparser/aml/grammar.py
Normal file
494
misc/config_tools/board_inspector/acpiparser/aml/grammar.py
Normal 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,)
|
585
misc/config_tools/board_inspector/acpiparser/aml/interpreter.py
Normal file
585
misc/config_tools/board_inspector/acpiparser/aml/interpreter.py
Normal 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)
|
537
misc/config_tools/board_inspector/acpiparser/aml/parser.py
Normal file
537
misc/config_tools/board_inspector/acpiparser/aml/parser.py
Normal 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
|
113
misc/config_tools/board_inspector/acpiparser/aml/stream.py
Normal file
113
misc/config_tools/board_inspector/acpiparser/aml/stream.py
Normal 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)
|
111
misc/config_tools/board_inspector/acpiparser/aml/tree.py
Normal file
111
misc/config_tools/board_inspector/acpiparser/aml/tree.py
Normal 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}")
|
68
misc/config_tools/board_inspector/acpiparser/aml/visitors.py
Normal file
68
misc/config_tools/board_inspector/acpiparser/aml/visitors.py
Normal 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)
|
42
misc/config_tools/board_inspector/acpiparser/dsdt.py
Normal file
42
misc/config_tools/board_inspector/acpiparser/dsdt.py
Normal 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
|
622
misc/config_tools/board_inspector/acpiparser/rdt.py
Normal file
622
misc/config_tools/board_inspector/acpiparser/rdt.py
Normal 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)
|
27
misc/config_tools/board_inspector/lib/mmio.py
Normal file
27
misc/config_tools/board_inspector/lib/mmio.py
Normal 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]))
|
Loading…
Reference in New Issue
Block a user