board_inspector: add builders of AML AST nodes

This patch adds the acpiparser.aml.builder module which provides methods to
construct AML trees from scratch in Python. Similar to how parsers and
binary generators are implemented, this module constructs most builder
methods from the AML grammar defined in the acpiparser.aml.grammar
module. AML objects whose grammar are not present in the grammar module
require special treatment and their builders are implemented
explicitly. The methods have the same name as the AML tree labels defined
in the grammar.

In addition, this module also provides the method `build_value` which
converts plain integers, strings or interpreter values (which are defined
in the datatypes module) to AML trees.

With the builders, the `interpret_method_call` method in the
ConcreteInterpreter is refined to build the (fake) MethodInvocation node
using the builders and handle the actual parameters as well.

Tracked-On: #6287
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
Junjie Mao 2021-07-26 15:21:37 +08:00 committed by wenlingz
parent e91ace7341
commit a3aa0797b1
2 changed files with 152 additions and 6 deletions

View File

@ -0,0 +1,140 @@
# Copyright (C) 2021 Intel Corporation. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
from . import grammar
from . import datatypes
from .tree import Tree
### Basic data types
def __build_value(label, value):
tree = Tree(label, [value])
tree.register_structure(("value",))
tree.complete_parsing()
return tree
def __build_string(label, s):
assert isinstance(s, str)
return __build_value(label, s)
def __build_const_data(label, data):
assert isinstance(data, int)
return __build_value(label, data)
NameSeg = lambda x: __build_string("NameSeg", x)
NameString = lambda x: __build_string("NameString", x)
String = lambda x: __build_string("String", x)
def ByteList(data):
assert isinstance(data, (bytes, bytearray))
return __build_value("ByteList", data)
ByteData = lambda x: __build_const_data("ByteData", x)
WordData = lambda x: __build_const_data("WordData", x)
DWordData = lambda x: __build_const_data("DWordData", x)
TWordData = lambda x: __build_const_data("TWordData", x)
QWordData = lambda x: __build_const_data("QWordData", x)
def PkgLength(length=0):
return __build_const_data("PkgLength", length)
FieldLength = lambda x: __build_const_data("FieldLength", x)
### Sequences
def MethodInvocation(name, *args):
if isinstance(name, str):
name_tree = NameString(name)
else:
name_tree = name
tree = Tree("MethodInvocation", [name])
for arg in args:
assert isinstance(arg, Tree)
tree.append_child(arg)
tree.register_structure(("NameString", "TermArg*"))
tree.complete_parsing()
return tree
def __create_sequence_builder(label):
def aux(tree, elem, arg):
if isinstance(arg, Tree):
# TODO: validate the given arg
tree.append_child(arg)
else:
tree.append_child(globals()[elem](arg))
seq = grammar.get_definition(label)
structure = grammar.get_names(label)
def fn(*args):
tree = Tree(label)
it = iter(args)
for elem in seq:
if isinstance(elem, int): # The leading opcode
continue
elif elem.endswith("*"):
for arg in it:
aux(tree, elem, arg)
elif elem.endswith("?"):
try:
aux(tree, elem, next(it))
except StopIteration:
pass
else:
aux(tree, elem, next(it))
tree.register_structure(structure)
tree.complete_parsing()
return tree
return fn
def build_value(value):
if isinstance(value, (int, datatypes.Integer)):
if isinstance(value, int):
value = datatypes.Integer(value)
v = value.get()
return \
ZeroOp() if v == 0 else \
OneOp() if v == 1 else \
ByteConst(v) if v <= 0xff else \
WordConst(v) if v <= 0xffff else \
DWordConst(v) if v <= 0xffffffff else \
QWordConst(v)
elif isinstance(value, datatypes.Buffer):
buffer_size = len(value.get())
builder = ByteConst if buffer_size <= 0xff else \
WordConst if buffer_size <= 0xffff else \
DWordConst if buffer_size <= 0xffffffff else \
QWordConst
return DefBuffer(
PkgLength(),
builder(buffer_size),
ByteList(value.get()))
elif isinstance(value, datatypes.Package):
elements = list(map(build_value, value.elements))
return DefPackage(
PkgLength(),
len(value.elements),
PackageElementList(elements))
elif isinstance(value, (str, datatypes.String)):
if isinstance(value, str):
return String(value)
else:
return String(value.get())
elif isinstance(value, datatypes.BufferField):
return build_value(value.to_integer())
else:
return None
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):
globals()[sym] = __create_sequence_builder(sym)
elif isinstance(definition, list) and len(definition) == 1:
if definition[0] in globals().keys():
globals()[sym] = globals()[definition[0]]

View File

@ -6,6 +6,7 @@
from .context import *
from .datatypes import *
from .tree import Tree, Interpreter
from . import builder
class MethodReturn(Exception):
""" A pseudo exception to return from a method"""
@ -86,13 +87,18 @@ class ConcreteInterpreter(Interpreter):
def interpret_method_call(self, name, *args):
stack_depth_before = len(self.stack)
name_string = Tree("NameString", [name])
name_string.register_structure(("value",))
name_string.complete_parsing()
name_string = builder.NameString(name)
name_string.scope = self.context.parent(name)
pseudo_invocation = Tree("MethodInvocation", [name_string])
pseudo_invocation.register_structure(("NameString", "TermArg*"))
pseudo_invocation.complete_parsing()
arg_trees = []
for arg in args:
v = builder.build_value(arg)
if v != None:
arg_trees.append(v)
else:
raise NotImplementedError(f"Unsupported type of method argument: {arg}")
pseudo_invocation = builder.MethodInvocation(name_string, *arg_trees)
try:
val = self.interpret(pseudo_invocation)
except: