From 55554e7d566fcc190c2d03d546c629c9a9a4bb21 Mon Sep 17 00:00:00 2001 From: Junjie Mao Date: Sun, 18 Jul 2021 16:22:59 +0800 Subject: [PATCH] board_inspector: add a visitor to generate AML binary from trees This patch introduces a visitor that converts an arbitrary AML tree to an AML binary. Most nodes can be converted in a straightforward way by following the defined grammar, but the following nodes require some additional effort: - NameStrings can be formatted as either a NameSeg (i.e. four upper case characters), a DualNamePath, a MultiNamePath or a NullName. - PkgLengths are recalculated according to the actual length of the following object (in case they are changed dynamically after being generated by the parser) and generated following the AML encoding of such lengths. The visitor works in a bottom-up manner, i.e. the children are visited and converted to binary before the parent. The whole trees parsed from DSDT/SSDTs are now also stored in the Context for further reference. Tracked-On: #6287 Signed-off-by: Junjie Mao --- .../board_inspector/acpiparser/aml/context.py | 1 + .../board_inspector/acpiparser/aml/tree.py | 8 ++ .../acpiparser/aml/visitors.py | 124 ++++++++++++++++++ .../board_inspector/acpiparser/dsdt.py | 5 +- 4 files changed, 135 insertions(+), 3 deletions(-) diff --git a/misc/config_tools/board_inspector/acpiparser/aml/context.py b/misc/config_tools/board_inspector/acpiparser/aml/context.py index 44f5d0d60..cd59fa749 100644 --- a/misc/config_tools/board_inspector/acpiparser/aml/context.py +++ b/misc/config_tools/board_inspector/acpiparser/aml/context.py @@ -142,6 +142,7 @@ class Context: def __init__(self): self.streams = {} self.current_stream = None + self.trees = {} # Loaded namespace self.__symbol_table = {} diff --git a/misc/config_tools/board_inspector/acpiparser/aml/tree.py b/misc/config_tools/board_inspector/acpiparser/aml/tree.py index d33159222..80df5c614 100644 --- a/misc/config_tools/board_inspector/acpiparser/aml/tree.py +++ b/misc/config_tools/board_inspector/acpiparser/aml/tree.py @@ -63,6 +63,14 @@ class Visitor: self.visit_topdown(child) self.depth -= 1 + def visit_bottomup(self, tree): + self.depth += 1 + for child in tree.children: + if isinstance(child, Tree): + self.visit_bottomup(child) + self.depth -= 1 + self.__visit(tree) + class Transformer: def __init__(self): self.depth = 0 diff --git a/misc/config_tools/board_inspector/acpiparser/aml/visitors.py b/misc/config_tools/board_inspector/acpiparser/aml/visitors.py index f64e04981..d455169ba 100644 --- a/misc/config_tools/board_inspector/acpiparser/aml/visitors.py +++ b/misc/config_tools/board_inspector/acpiparser/aml/visitors.py @@ -3,7 +3,10 @@ # SPDX-License-Identifier: BSD-3-Clause # +import sys + from .tree import Visitor +from . import grammar class PrintLayoutVisitor(Visitor): @staticmethod @@ -69,3 +72,124 @@ class ConditionallyUnregisterSymbolVisitor(Visitor): self.depth -= 1 elif tree.label not in ["DefMethod"]: super().visit_topdown(tree) + +class GenerateBinaryVisitor(Visitor): + def __init__(self): + super().__init__() + + @staticmethod + def __format_length(length): + assert length <= 0x0FFFFFFF + if length <= 0x3F: + return length.to_bytes(1, sys.byteorder) + else: + first_byte_value = length & 0xF + rest_byte_value = length >> 4 + if rest_byte_value <= 0xFF: + first_byte = (first_byte_value + (1 << 6)).to_bytes(1, sys.byteorder) + rest_bytes = rest_byte_value.to_bytes(1, sys.byteorder) + elif rest_byte_value <= 0xFFFF: + first_byte = (first_byte_value + (2 << 6)).to_bytes(1, sys.byteorder) + rest_bytes = rest_byte_value.to_bytes(2, sys.byteorder) + else: + first_byte = (first_byte_value + (3 << 6)).to_bytes(1, sys.byteorder) + rest_bytes = rest_byte_value.to_bytes(3, sys.byteorder) + return first_byte + rest_bytes + + @staticmethod + def __format_pkg_length(length): + assert length <= 0x0FFFFFFB + if length <= 0x3E: + length += 1 + elif length <= 0x0FFD: + length += 2 + elif length <= 0x0FFFFC: + length += 3 + else: + length += 4 + return GenerateBinaryVisitor.__format_length(length) + + def generate(self, tree): + self.acc = [] + self.visit(tree) + assert len(self.acc) == 1 + return self.acc.pop() + + def ByteData(self, tree): + self.acc.append(tree.value.to_bytes(1, sys.byteorder)) + + def WordData(self, tree): + self.acc.append(tree.value.to_bytes(2, sys.byteorder)) + + def DWordData(self, tree): + self.acc.append(tree.value.to_bytes(4, sys.byteorder)) + + def TWordData(self, tree): + self.acc.append(tree.value.to_bytes(6, sys.byteorder)) + + def QWordData(self, tree): + self.acc.append(tree.value.to_bytes(8, sys.byteorder)) + + def NameSeg(self, tree): + name = tree.value[:4] + if len(name) < 4: + name += ("_" * (4 - len(name))) + self.acc.append(bytes(name, "ascii")) + + def NameString(self, tree): + acc = bytearray() + segs = tree.value.lstrip("^\\") + if len(segs) < len(tree.value): + acc.extend(bytes(tree.value[:len(tree.value) - len(segs)], "ascii")) + if segs: + nr_dots = segs.count(".") + if nr_dots == 1: + acc.append(grammar.AML_DUAL_NAME_PREFIX) + elif nr_dots >= 2: + acc.append(grammar.AML_MULTI_NAME_PREFIX) + acc.append(nr_dots + 1) + acc.extend(bytes(segs.replace(".", ""), "ascii")) + else: + acc.append(grammar.AML_NULL_NAME) + self.acc.append(acc) + + def String(self, tree): + acc = bytearray() + acc.append(grammar.AML_STRING_PREFIX) + acc.extend(bytes(tree.value, "latin-1")) + acc.append(0) + self.acc.append(acc) + + def ByteList(self, tree): + self.acc.append(tree.value) + + def PkgLength(self, tree): + pass + + def FieldLength(self, tree): + self.acc.append(self.__format_length(tree.value)) + + def default(self, tree): + assert tree.structure and isinstance(tree.structure, tuple) + assert tree.structure != ("value",), f"{tree.label} is not expected to be handled by the default handler" + + parts = [] + for child in reversed(tree.children): + if child.label == "PkgLength": + if tree.label == "DefIfElse" and tree.DefElse: + # The last component of a DefIfElse clause (i.e. DefElse) is not counted by the PkgLength of the + # DefIfElse. + length = sum(map(len, parts[1:])) + else: + length = sum(map(len, parts)) + parts.append(self.__format_pkg_length(length)) + else: + parts.append(self.acc.pop()) + if isinstance(tree.structure[0], int): + opcode = tree.structure[0] + if opcode <= 0xFF: + parts.append(opcode.to_bytes(1, sys.byteorder)) + else: + parts.append(opcode.to_bytes(2, sys.byteorder)) + + self.acc.append(b''.join(reversed(parts))) diff --git a/misc/config_tools/board_inspector/acpiparser/dsdt.py b/misc/config_tools/board_inspector/acpiparser/dsdt.py index 9f3eba813..fbc282bbd 100644 --- a/misc/config_tools/board_inspector/acpiparser/dsdt.py +++ b/misc/config_tools/board_inspector/acpiparser/dsdt.py @@ -21,7 +21,6 @@ def DSDT(val): 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}") @@ -29,13 +28,13 @@ def DSDT(val): tree = Tree() AMLCode.parse(context, tree) tree = DeferredExpansion(context).transform_topdown(tree) - trees.append(tree) + context.trees[os.path.basename(t)] = tree except Exception as e: context.current_stream.dump() raise context.skip_external_on_lookup() visitor = ConditionallyUnregisterSymbolVisitor(ConcreteInterpreter(context)) - for tree in trees: + for tree in context.trees.values(): visitor.visit_topdown(tree) return context