board_inspector: improve readability and performance of AML parser

This patch refines the AML parser to improve its readability and
performance in the following ways.

  1. A Tree object now has the parts of the corresponding object being
     member fields. As an example, a Tree with label `DefMethod` now has
     members `NameString`, `MethodFlags` and `TermList`.
  2. It is now possible to assign names each part of an object. The grammar
     is updated to assign different names to the parts with the same type
     in the same object.

Tracked-On: #6298
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
Junjie Mao 2021-07-16 14:47:31 +08:00 committed by Xie, Nanlin
parent 6e79479a0a
commit 60da7d6bfd
8 changed files with 328 additions and 268 deletions

View File

@ -57,13 +57,13 @@ class OperationFieldDecl(NamedDecl):
print(f"{self.name}: {self.__class__.__name__}, {self.length} bits")
class AliasDecl(NamedDecl):
def __init__(self, name, target, tree):
def __init__(self, name, source, tree):
super().__init__(name, tree)
self.name = name
self.target = target
self.source = source
def dump(self):
print(f"{self.name}: {self.__class__.__name__}, aliasing {self.target}")
print(f"{self.name}: {self.__class__.__name__}, aliasing {self.source}")
class MethodDecl(NamedDecl):
def __init__(self, name, nargs, tree):

View File

@ -48,10 +48,10 @@ class BufferBase(Object):
self.__length = length
self.__fields = {} # name -> (offset, bitwidth, access_width)
def read(self, norm_idx, bit_width):
def read(self, byte_idx, bit_width):
return NotImplementedError(self.__class__.__name__)
def write(self, norm_idx, value, bit_width):
def write(self, byte_idx, value, bit_width):
return NotImplementedError(self.__class__.__name__)
def create_field(self, name, offset, bitwidth, access_width):
@ -69,23 +69,23 @@ class BufferBase(Object):
# Bits out of byte boundary
if bit_idx % access_width > 0:
norm_idx = floor(bit_idx / access_width)
byte_idx = bit_idx // 8
bit_count = (access_width - bit_idx % access_width)
if bit_count > bit_remaining:
bit_count = bit_remaining
mask = self.bitmask(bit_idx % access_width + bit_count - 1, bit_idx % access_width)
acc = (self.read(norm_idx, access_width) & mask) >> (bit_idx % access_width)
acc = (self.read(byte_idx, access_width) & mask) >> (bit_idx % access_width)
acc_bit_count += bit_count
bit_idx += bit_count
bit_remaining -= bit_count
while bit_remaining > 0:
norm_idx = floor(bit_idx / access_width)
bit_count = access_width if bit_remaining >= access_width else bit_remaining
byte_idx = bit_idx // 8
bit_count = min(access_width, bit_remaining)
mask = self.bitmask(bit_count - 1, 0)
acc |= (self.read(norm_idx, access_width) & mask) << acc_bit_count
acc |= (self.read(byte_idx, access_width) & mask) << acc_bit_count
acc_bit_count += bit_count
bit_idx += bit_count
bit_remaining -= bit_count
@ -100,9 +100,9 @@ class BufferBase(Object):
assert offset + bitwidth <= self.__length * 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
# Bits out of access_width boundary
if bit_idx % access_width > 0:
norm_idx = floor(bit_idx / access_width)
byte_idx = bit_idx // 8
bit_count = (access_width - bit_idx % access_width)
if bit_count > bit_remaining:
bit_count = bit_remaining
@ -110,20 +110,20 @@ class BufferBase(Object):
mask_of_write = self.bitmask(bit_idx % access_width + bit_count - 1, bit_idx % access_width)
mask_of_keep = ((1 << access_width) - 1) - mask_of_write
v = (value & ((1 << bit_count) - 1)) << (bit_idx % access_width)
self.write(norm_idx, (v & mask_of_write) | (self.read(norm_idx, access_width) & mask_of_keep), access_width)
self.write(byte_idx, (v & mask_of_write) | (self.read(byte_idx, access_width) & mask_of_keep), access_width)
value >>= bit_count
bit_idx += bit_count
bit_remaining -= bit_count
while bit_remaining > 0:
norm_idx = floor(bit_idx / access_width)
bit_count = access_width if bit_remaining >= access_width else bit_remaining
byte_idx = bit_idx // 8
bit_count = min(access_width, bit_remaining)
mask_of_write = self.bitmask(bit_count - 1, 0)
mask_of_keep = ((1 << access_width) - 1) - mask_of_write
v = (value & ((1 << bit_count) - 1))
self.write(norm_idx, (v & mask_of_write) | (self.read(norm_idx, access_width) & mask_of_keep), access_width)
self.write(byte_idx, (v & mask_of_write) | (self.read(byte_idx, access_width) & mask_of_keep), access_width)
value >>= bit_count
bit_idx += bit_count
@ -142,16 +142,14 @@ class Buffer(BufferBase):
def data(self):
return bytes(self.__data)
def read(self, norm_idx, bit_width):
def read(self, byte_idx, bit_width):
acc = 0
byte_width = bit_width // 8
offset = norm_idx * byte_width
return int.from_bytes(self.__data[offset : (offset + byte_width)], sys.byteorder)
byte_width = min(bit_width // 8, len(self.__data) - byte_idx)
return int.from_bytes(self.__data[byte_idx : (byte_idx + byte_width)], sys.byteorder)
def write(self, norm_idx, value, bit_width):
byte_width = bit_width // 8
offset = norm_idx * byte_width
self.__data[offset : (offset + byte_width)] = value.to_bytes(byte_width, sys.byteorder)
def write(self, byte_idx, value, bit_width):
byte_width = min(bit_width // 8, len(self.__data) - byte_idx)
self.__data[byte_idx : (byte_idx + byte_width)] = value.to_bytes(byte_width, sys.byteorder)
def get(self):
return self.__data
@ -175,18 +173,16 @@ class StreamIOBuffer(BufferBase):
self.__stream = stream
self.__base = base
def read(self, norm_idx, bit_width):
def read(self, byte_idx, bit_width):
byte_width = bit_width // 8
address = norm_idx * byte_width
self.__stream.seek(self.__base + address)
self.__stream.seek(self.__base + byte_idx)
data = self.__stream.read(byte_width)
return int.from_bytes(data, sys.byteorder)
def write(self, norm_idx, value, bit_width):
def write(self, byte_idx, value, bit_width):
# Do not allow writes to stream I/O buffer unless the base is explicitly marked as writable
byte_width = bit_width // 8
address = norm_idx * byte_width
self.__stream.seek(self.__base + address)
self.__stream.seek(self.__base + byte_idx)
self.__stream.write(value.to_bytes(byte_width, sys.byteorder))
class IndexedIOBuffer(BufferBase):
@ -196,12 +192,12 @@ class IndexedIOBuffer(BufferBase):
self.__index_register = index_register
self.__data_register = data_register
def read(self, norm_idx, bit_width):
def read(self, byte_idx, bit_width):
assert bit_width == 8, f"Indexed I/O buffers can only be read one byte at a time"
self.__index_register.set(Integer(norm_idx, 8))
self.__index_register.set(Integer(byte_idx, 8))
return self.__data_register.get()
def write(self, norm_idx, value, bit_width):
def write(self, byte_idx, value, bit_width):
# Do not allow writes to indexed I/O buffer
assert False, "Cannot write to indexed I/O buffers"
@ -274,7 +270,7 @@ class Integer(Object):
class Method(Object):
def __init__(self, tree):
self.tree = tree
self.name = tree.children[1].children
self.name = tree.children[1].value
self.body = tree.children[3]
class PredefinedMethod(Object):

View File

@ -136,15 +136,15 @@ AML_DATA_REGION_OP = 0x885b
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",)
TableSignature = ["DWordData"]
TableLength = ["DWordData"]
SpecCompliance = ["ByteData"]
CheckSum = ["ByteData"]
OemID = ["TWordData"]
OemTableID = ["QWordData"]
OemRevision = ["DWordData"]
CreatorID = ["DWordData"]
CreatorRevision = ["DWordData"]
################################################################################
# 20.2.2 Name Objects Encoding
@ -202,7 +202,7 @@ TermArg = ["ExpressionOpcode", "DataObject", "ArgObj", "LocalObj"]
NameSpaceModifierObj = ["DefAlias", "DefName", "DefScope"]
DefAlias = (AML_ALIAS_OP, "NameString", "NameString")
DefAlias = (AML_ALIAS_OP, "NameString:SourceObject", "NameString:AliasObject")
DefName = (AML_NAME_OP, "NameString", "DataRefObject")
DefScope = (AML_SCOPE_OP, "PkgLength", "NameString", "TermList")
@ -214,68 +214,68 @@ NamedObj = ["DefBankField", "DefCreateBitField", "DefCreateByteField", "DefCreat
"DefField", "DefIndexField", "DefMethod", "DefMutex", "DefOpRegion", "DefPowerRes", "DefProcessor",
"DefThermalZone"]
DefBankField = (AML_BANK_FIELD_OP, "PkgLength", "NameString", "NameString", "BankValue", "FieldFlags", "FieldList")
BankValue = ("TermArg",)
FieldFlags = ("ByteData",)
DefBankField = (AML_BANK_FIELD_OP, "PkgLength", "NameString:RegionName", "NameString:BankName", "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"]
AccessType = ["ByteData"]
AccessAttrib = ["ByteData"]
ConnectFieldDef = ["NameString"] # FIXME: ACPI spec allows "BufferData" here but does not define what "BufferData" is
ConnectField = (AML_CONNECT_FIELD_PREFIX, "ConnectFieldDef")
DefCreateBitField = (AML_CREATE_BIT_FIELD_OP, "SourceBuff", "BitIndex", "NameString")
SourceBuff = ("TermArg",)
BitIndex = ("TermArg",)
SourceBuff = ["TermArg"]
BitIndex = ["TermArg"]
DefCreateByteField = (AML_CREATE_BYTE_FIELD_OP, "SourceBuff", "ByteIndex", "NameString")
ByteIndex = ("TermArg",)
ByteIndex = ["TermArg"]
DefCreateDWordField = (AML_CREATE_DWORD_FIELD_OP, "SourceBuff", "ByteIndex", "NameString")
DefCreateField = (AML_CREATE_FIELD_OP, "SourceBuff", "BitIndex", "NumBits", "NameString")
NumBits = ("TermArg",)
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")
DefDataRegion = (AML_DATA_REGION_OP, "NameString", "TermArg:Signature", "TermArg:OEMID", "TermArg:OEMTableID")
DefDevice = (AML_DEVICE_OP, "PkgLength", "NameString", "TermList")
DefEvent = (AML_EVENT_OP, "NameString")
DefExternal = (AML_EXTERNAL_OP, "NameString", "ObjectType", "ArgumentCount")
ObjectType = ("ByteData",)
ArgumentCount = ("ByteData",)
ObjectType = ["ByteData"]
ArgumentCount = ["ByteData"]
DefField = (AML_FIELD_OP, "PkgLength", "NameString", "FieldFlags", "FieldList")
DefIndexField = (AML_INDEX_FIELD_OP, "PkgLength", "NameString", "NameString", "FieldFlags", "FieldList")
DefIndexField = (AML_INDEX_FIELD_OP, "PkgLength", "NameString:IndexName", "NameString:DataName", "FieldFlags", "FieldList")
DefMethod = (AML_METHOD_OP, "PkgLength", "NameString", "MethodFlags", "TermList")
MethodFlags = ("ByteData",)
MethodFlags = ["ByteData"]
DefMutex = (AML_MUTEX_OP, "NameString", "SyncFlags")
SyncFlags = ("ByteData",)
SyncFlags = ["ByteData"]
DefOpRegion = (AML_REGION_OP, "NameString", "RegionSpace", "RegionOffset", "RegionLen")
RegionSpace = ("ByteData",)
RegionOffset = ("TermArg",)
RegionLen = ("TermArg",)
RegionSpace = ["ByteData"]
RegionOffset = ["TermArg"]
RegionLen = ["TermArg"]
DefPowerRes = (AML_POWER_RESOURCE_OP, "PkgLength", "NameString", "SystemLevel", "ResourceOrder", "TermList")
SystemLevel = ("ByteData",)
ResourceOrder = ("WordData",)
SystemLevel = ["ByteData"]
ResourceOrder = ["WordData"]
DefProcessor = (AML_PROCESSOR_OP, "PkgLength", "NameString", "ProcID", "PblkAddr", "PblkLen", "TermList")
ProcID = ("ByteData",)
PblkAddr = ("DWordData",)
PblkLen = ("ByteData",)
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",)
ExtendedAccessAttrib = ["ByteData"]
AccessLength = ["ByteData"]
FieldElement = ["NamedField", "ReservedField", "AccessField", "ExtendedAccessField", "ConnectField"]
# 20.2.5.3 Statement Opcodes Encoding
@ -293,35 +293,35 @@ DefContinue = (AML_CONTINUE_OP,)
DefElse = (AML_ELSE_OP, "PkgLength", "TermList")
DefFatal = (AML_FATAL_OP, "FatalType", "FatalCode", "FataArg")
FatalType = ("ByteData",)
FatalCode = ("DWordData",)
FatalArg = ("TermArg",)
FatalType = ["ByteData"]
FatalCode = ["DWordData"]
FatalArg = ["TermArg"]
DefIfElse = (AML_IF_OP, "PkgLength", "Predicate", "TermList", "DefElse?")
Predicate = ("TermArg",)
Predicate = ["TermArg"]
DefNoop = (AML_NOOP_OP,)
DefNotify = (AML_NOTIFY_OP, "NotifyObject", "NotifyValue")
NotifyObject = ("SuperName",)
NotifyValue = ("TermArg",)
NotifyObject = ["SuperName"]
NotifyValue = ["TermArg"]
DefRelease = (AML_RELEASE_OP, "MutexObject")
MutexObject = ("SuperName",)
MutexObject = ["SuperName"]
DefReset = (AML_RESET_OP, "EventObject")
EventObject = ("SuperName",)
EventObject = ["SuperName"]
DefReturn = (AML_RETURN_OP, "ArgObject")
ArgObject = ("TermArg",)
ArgObject = ["TermArg"]
DefSignal = (AML_SIGNAL_OP, "EventObject")
DefSleep = (AML_SLEEP_OP, "MsecTime")
MsecTime = ("TermArg",)
MsecTime = ["TermArg"]
DefStall = (AML_STALL_OP, "UsecTime")
UsecTime = ("TermArg",)
UsecTime = ["TermArg"]
DefUnload = (AML_UNLOAD_OP, "Target")
@ -342,21 +342,21 @@ ExpressionOpcode = ["DefAcquire", "DefAdd", "DefAnd", "DefBuffer", "DefConcat",
ReferenceTypeOpcode = ["DefRefOf", "DefDerefOf", "DefIndex"]
DefAcquire = (AML_ACQUIRE_OP, "MutexObject", "Timeout")
Timeout = ("WordData",)
Timeout = ["WordData"]
DefAdd = (AML_ADD_OP, "Operand", "Operand", "Target")
DefAdd = (AML_ADD_OP, "Operand:LeftOperand", "Operand:RightOperand", "Target")
Operand = ["TermArg"]
DefAnd = (AML_AND_OP, "Operand", "Operand", "Target")
DefAnd = (AML_AND_OP, "Operand:LeftOperand", "Operand:RightOperand", "Target")
DefBuffer = (AML_BUFFER_OP, "PkgLength", "BufferSize", "ByteList")
BufferSize = ("TermArg",)
BufferSize = ["TermArg"]
DefConcat = (AML_CONCAT_OP, "Data", "Data", "Target")
Data = ("TermArg",)
DefConcat = (AML_CONCAT_OP, "Data:Data1", "Data:Data2", "Target")
Data = ["TermArg"]
DefConcatRes = (AML_CONCAT_RES_OP, "BufData", "BufData", "Target")
BufData = ("TermArg",)
DefConcatRes = (AML_CONCAT_RES_OP, "BufData:BufData1", "BufData:BufData2", "Target")
BufData = ["TermArg"]
DefCondRefOf = (AML_CONDITIONAL_REF_OF_OP, "SuperName", "Target")
@ -365,80 +365,80 @@ DefCopyObject = (AML_COPY_OBJECT_OP, "TermArg", "SimpleName")
DefDecrement = (AML_DECREMENT_OP, "SuperName")
DefDerefOf = (AML_DEREF_OF_OP, "ObjReference")
ObjReference = ("TermArg",)
ObjReference = ["TermArg"]
DefDivide = (AML_DIVIDE_OP, "Dividend", "Divisor", "Remainder", "Quotient")
Dividend = ("TermArg",)
Divisor = ("TermArg",)
Remainder = ("Target",)
Quotient = ("Target",)
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",)
BCDValue = ["TermArg"]
DefIncrement = (AML_INCREMENT_OP, "SuperName")
DefIndex = (AML_INDEX_OP, "BuffPkgStrObj", "IndexValue", "Target")
BuffPkgStrObj = ("TermArg",)
IndexValue = ("TermArg",)
BuffPkgStrObj = ["TermArg"]
IndexValue = ["TermArg"]
DefLAnd = (AML_LAND_OP, "Operand", "Operand")
DefLEqual = (AML_LEQUAL_OP, "Operand", "Operand")
DefLGreater = (AML_LGREATER_OP, "Operand", "Operand")
DefLAnd = (AML_LAND_OP, "Operand:LeftOperand", "Operand:RightOperand")
DefLEqual = (AML_LEQUAL_OP, "Operand:LeftOperand", "Operand:RightOperand")
DefLGreater = (AML_LGREATER_OP, "Operand:LeftOperand", "Operand:RightOperand")
# DefLGreaterEqual is equivalent to (AML_LNOT_OP, DefLLess)
DefLLess = (AML_LLESS_OP, "Operand", "Operand")
DefLLess = (AML_LLESS_OP, "Operand:LeftOperand", "Operand:RightOperand")
# 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")
DefLoadTable = (AML_LOAD_TABLE_OP, "TermArg:Signature", "TermArg:OEMID", "TermArg:TableID", "TermArg:RootPath", "TermArg:ParameterPath", "TermArg:ParameterData")
DefLOr = (AML_LOR_OP, "Operand", "Operand")
DefLOr = (AML_LOR_OP, "Operand:LeftOperand", "Operand:RightOperand")
DefMatch = (AML_MATCH_OP, "SearchPkg", "MatchOpcode", "Operand", "MatchOpcode", "Operand", "StartIndex")
SearchPkg = ("TermArg",)
MatchOpcode = ("ByteData",)
StartIndex = ("TermArg",)
DefMatch = (AML_MATCH_OP, "SearchPkg", "MatchOpcode:MatchOpcode1", "Operand:Operand1", "MatchOpcode:MatchOpcode2", "Operand:Operand2", "StartIndex")
SearchPkg = ["TermArg"]
MatchOpcode = ["ByteData"]
StartIndex = ["TermArg"]
DefMid = (AML_MID_OP, "MidObj", "TermArg", "TermArg", "Target")
MidObj = ("TermArg",)
DefMid = (AML_MID_OP, "MidObj", "TermArg:Source", "TermArg:Index", "Target")
MidObj = ["TermArg"]
DefMod = (AML_MOD_OP, "Dividend", "Divisor", "Target")
DefMultiply = (AML_MULTIPLY_OP, "Operand", "Operand", "Target")
DefMultiply = (AML_MULTIPLY_OP, "Operand:LeftOperand", "Operand:RightOperand", "Target")
DefNAnd = (AML_NAND_OP, "Operand", "Operand", "Target")
DefNOr = (AML_NOR_OP, "Operand", "Operand", "Target")
DefNAnd = (AML_NAND_OP, "Operand:LeftOperand", "Operand:RightOperand", "Target")
DefNOr = (AML_NOR_OP, "Operand:LeftOperand", "Operand:RightOperand", "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")
DefOr = (AML_OR_OP, "Operand:LeftOperand", "Operand:RightOperand", "Target")
DefPackage = (AML_PACKAGE_OP, "PkgLength", "NumElements", "PackageElementList")
DefVarPackage = (AML_VAR_PACKAGE_OP, "PkgLength", "VarNumElements", "PackageElementList")
NumElements = ("ByteData",)
VarNumElements = ("TermArg",)
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",)
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")
DefSubtract = (AML_SUBTRACT_OP, "Operand:LeftOperand", "Operand:RightOperand", "Target")
DefTimer = (AML_TIMER_OP,)
@ -453,11 +453,11 @@ 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",)
LengthArg = ["TermArg"]
DefWait = (AML_WAIT_OP, "EventObject", "Operand")
DefXOr = (AML_XOR_OP, "Operand", "Operand", "Target")
DefXOr = (AML_XOR_OP, "Operand:LeftOperand", "Operand:RightOperand", "Target")
################################################################################
# 20.2.6 Miscellaneous Objects Encoding
@ -492,3 +492,24 @@ Local7Op = (AML_LOCAL7_OP,)
################################################################################
DebugObj = (AML_DEBUG_OP,)
################################################################################
# Helper methods
################################################################################
def __get_spec(sym, fn):
spec = globals()[sym]
if isinstance(spec, tuple):
return tuple(map(fn, spec))
else:
return spec
def get_definition(sym):
def __get_definition(elem):
return elem.split(":")[0] if isinstance(elem, str) else elem
return __get_spec(sym, __get_definition)
def get_names(sym):
def __get_names(elem):
return elem.split(":")[-1] if isinstance(elem, str) else elem
return __get_spec(sym, __get_names)

View File

@ -86,9 +86,13 @@ class ConcreteInterpreter(Interpreter):
def interpret_method_call(self, name, *args):
stack_depth_before = len(self.stack)
name_string = Tree("NameString", name)
name_string = Tree("NameString", [name])
name_string.register_structure(("value",))
name_string.complete_parsing()
name_string.scope = self.context.parent(name)
pseudo_invocation = Tree("MethodInvocation", [name_string])
pseudo_invocation.register_structure(("NameString", "TermArg*"))
pseudo_invocation.complete_parsing()
try:
val = self.interpret(pseudo_invocation)
except:
@ -118,7 +122,7 @@ class ConcreteInterpreter(Interpreter):
return None
def NameString(self, tree):
name = tree.children
name = tree.value
obj = self.context.lookup_binding(name)
if not obj:
sym = self.context.lookup_symbol(name)
@ -133,7 +137,7 @@ class ConcreteInterpreter(Interpreter):
# 20.2.3 Data Objects Encoding
def ByteList(self, tree):
return RawDataBuffer(tree.children)
return RawDataBuffer(tree.value)
def ByteConst(self, tree):
return self.interpret(tree.children[0])
@ -148,19 +152,19 @@ class ConcreteInterpreter(Interpreter):
return self.interpret(tree.children[0])
def String(self, tree):
return String(tree.children)
return String(tree.value)
def ByteData(self, tree):
return Integer(tree.children)
return Integer(tree.value)
def WordData(self, tree):
return Integer(tree.children)
return Integer(tree.value)
def DWordData(self, tree):
return Integer(tree.children)
return Integer(tree.value)
def QWordData(self, tree):
return Integer(tree.children)
return Integer(tree.value)
def ZeroOp(self, tree):
return Integer(0x00)
@ -210,7 +214,7 @@ class ConcreteInterpreter(Interpreter):
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}"
f"{tree.children[0]} evaluates to a non-object value {value}"
return value
# 20.2.5.1 Namespace Modifier Objects Encoding
@ -219,7 +223,7 @@ class ConcreteInterpreter(Interpreter):
def DefName(self, tree):
self.context.change_scope(tree.children[0].scope)
name = tree.children[0].children
name = tree.children[0].value
obj = self.context.lookup_binding(name)
if not obj:
obj = self.interpret(tree.children[1])
@ -229,7 +233,7 @@ class ConcreteInterpreter(Interpreter):
# 20.2.5.2 Named Objects Encoding
def NamedField(self, tree):
name = tree.children[0].children
name = tree.children[0].value
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."
@ -253,7 +257,7 @@ class ConcreteInterpreter(Interpreter):
buf = self.interpret(tree.children[0])
assert isinstance(buf, Buffer)
index = self.interpret(tree.children[1]).get()
name = tree.children[name_idx].children
name = tree.children[name_idx].value
if bitwidth == 1 or name_idx == 3:
buf.create_field(name, index, bitwidth, 8)
else:
@ -283,13 +287,13 @@ class ConcreteInterpreter(Interpreter):
return self.create_field(tree, numbits, 3)
def DefDevice(self, tree):
name = tree.children[1].children
name = tree.children[1].value
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}")
logging.info(f"The loaded tables do not have a definition of {tree.children[0].value}")
return None
def DefField(self, tree):
@ -300,7 +304,7 @@ class ConcreteInterpreter(Interpreter):
return Method(tree)
def DefOpRegion(self, tree):
name = tree.children[0].children
name = tree.children[0].value
sym = self.context.lookup_symbol(self.context.realpath(tree.scope, name))
space = self.interpret(tree.children[1]).get()

View File

@ -56,7 +56,8 @@ class Factory:
self.mark_end()
return tree
def opcodes(self):
@property
def decoder(self):
raise NotImplementedError
################################################################################
@ -66,17 +67,20 @@ class Factory:
class NameSegFactory(Factory):
def __init__(self):
super().__init__()
self.__opcodes = []
self.__decoder = {}
for i in range(ord('A'), ord('Z') + 1):
self.__opcodes.append(i)
self.__opcodes.append(ord('_'))
self.__decoder[i] = self
self.__decoder[ord('_')] = self
self.label = "NameSeg"
def match(self, context, stream, tree):
tree.children = stream.get_fixed_length_string(4)
tree.register_structure(("value",))
tree.append_child(stream.get_fixed_length_string(4))
tree.complete_parsing()
def opcodes(self):
return self.__opcodes
@property
def decoder(self):
return self.__decoder
NameSeg = NameSegFactory()
@ -84,12 +88,14 @@ class NameStringFactory(Factory):
def __init__(self):
super().__init__()
self.label = "NameString"
self.__opcodes = []
self.__decoder = {}
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])
self.__decoder[i] = self
for i in [ord('_'), ord('\\'), ord('^'), grammar.AML_DUAL_NAME_PREFIX, grammar.AML_MULTI_NAME_PREFIX]:
self.__decoder[i] = self
def match(self, context, stream, tree):
tree.register_structure(("value",))
acc = ""
# Namespace prefixes
@ -117,10 +123,12 @@ class NameStringFactory(Factory):
stream.seek(-1)
acc += stream.get_fixed_length_string(4)
tree.children = acc
tree.append_child(acc)
tree.complete_parsing()
def opcodes(self):
return self.__opcodes
@property
def decoder(self):
return self.__decoder
NameString = NameStringFactory()
@ -135,12 +143,11 @@ class ConstDataFactory(Factory):
self.width = width
def match(self, context, stream, tree):
tree.children = stream.get_integer(self.width)
tree.register_structure(("value",))
tree.append_child(stream.get_integer(self.width))
tree.complete_parsing()
return tree
def opcodes(self):
return None
ByteData = ConstDataFactory("ByteData", 1)
WordData = ConstDataFactory("WordData", 2)
DWordData = ConstDataFactory("DWordData", 4)
@ -155,11 +162,14 @@ class StringFactory(Factory):
def match(self, context, stream, tree):
assert stream.get_opcode()[0] == grammar.AML_STRING_PREFIX
tree.children = stream.get_string()
tree.register_structure(("value",))
tree.append_child(stream.get_string())
tree.complete_parsing()
return tree
def opcodes(self):
return [grammar.AML_STRING_PREFIX]
@property
def decoder(self):
return {grammar.AML_STRING_PREFIX: self}
String = StringFactory()
@ -169,7 +179,9 @@ class ByteListFactory(Factory):
self.label = "ByteList"
def match(self, context, stream, tree):
tree.children = stream.get_buffer()
tree.register_structure(("value",))
tree.append_child(stream.get_buffer())
tree.complete_parsing()
stream.pop_scope()
ByteList = ByteListFactory()
@ -200,10 +212,12 @@ class PkgLengthFactory(Factory):
byte_count = pkg_lead_byte >> 6
assert byte_count <= 3
tree.children = self.get_package_length(byte_count, stream.get_integer(byte_count + 1))
tree.register_structure(("value",))
tree.append_child(self.get_package_length(byte_count, stream.get_integer(byte_count + 1)))
tree.complete_parsing()
if self.create_new_scope:
remaining = tree.children - byte_count - 1
remaining = tree.value - byte_count - 1
stream.push_scope(remaining)
tree.package_range = (stream.current, remaining)
return tree
@ -218,27 +232,33 @@ FieldLength = PkgLengthFactory("FieldLength", False)
class MethodInvocationFactory(Factory):
def __init__(self):
super().__init__()
self.__opcodes = None
self.__decoder = None
self.label = "MethodInvocation"
def match(self, context, stream, tree):
tree.register_structure(("NameString", "TermArg*"))
child_namestring = Tree()
globals()["NameString"].parse(context, child_namestring)
tree.append_child(child_namestring)
sym = context.lookup_symbol(child_namestring.children)
sym = context.lookup_symbol(child_namestring.value)
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)
tree.complete_parsing()
return tree
def opcodes(self):
if not self.__opcodes:
self.__opcodes = globals()["NameString"].opcodes()
return self.__opcodes
@property
def decoder(self):
if not self.__decoder:
self.__decoder = {}
for k in globals()["NameString"].decoder.keys():
self.__decoder[k] = self
return self.__decoder
MethodInvocation = MethodInvocationFactory()
@ -250,15 +270,31 @@ class SequenceFactory(Factory):
def __init__(self, label, seq):
super().__init__()
self.label = label
# Some objects in ACPI AML have multiple occurrences of the same type of object in the grammar. In order to
# refer to these different occurrences, the grammar module uses the following notation to give names to each of
# them:
#
# "<object type>:<alias name>"
#
# The grammar module provides the get_definition() and get_names() methods to get the specification solely in
# object types or alias names, respectively. For objects without aliases, the type is reused as the name.
try:
self.seq = grammar.get_definition(label)
self.structure = grammar.get_names(label)
except KeyError:
self.seq = seq
self.__opcodes = None
self.structure = seq
self.__decoder = None
def match(self, context, stream, tree):
tree.register_structure(self.structure)
# 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()
tree.complete_parsing()
return tree
opcode, opcode_width = stream.peek_opcode()
@ -268,6 +304,7 @@ class SequenceFactory(Factory):
# cleanup actions upon exceptions.
to_recover_from_deferred_mode = False
to_pop_stream_scope = False
completed = True
for i,elem in enumerate(self.seq):
pos = stream.current
@ -288,7 +325,7 @@ class SequenceFactory(Factory):
factory = globals()[elem]
if not stream.at_end():
sub_opcode, _ = stream.peek_opcode()
if sub_opcode in factory.opcodes():
if sub_opcode in factory.decoder.keys():
child = Tree()
factory.parse(context, child)
tree.append_child(child)
@ -312,7 +349,7 @@ class SequenceFactory(Factory):
context.enter_deferred_mode()
to_recover_from_deferred_mode = True
elif child.label == "NameString":
self.hook_named(context, tree, child.children)
self.hook_named(context, tree, child.value)
except (DecodeError, DeferLater, ScopeMismatch, UndefinedSymbol) as e:
if to_pop_stream_scope:
stream.pop_scope(force=True)
@ -321,49 +358,54 @@ class SequenceFactory(Factory):
tree.context_scope = context.get_scope()
tree.factory = SequenceFactory(f"{self.label}.deferred", self.seq[i:])
stream.seek(package_end, absolute=True)
completed = False
break
else:
raise e
if completed:
tree.complete_parsing()
if to_recover_from_deferred_mode:
context.exit_deferred_mode()
return tree
def opcodes(self):
if not self.__opcodes:
@property
def decoder(self):
if not self.__decoder:
if isinstance(self.seq[0], int):
self.__opcodes = [self.seq[0]]
self.__decoder = {self.seq[0]: self}
else:
self.__opcodes = globals()[self.seq[0]].opcodes()
return self.__opcodes
self.__decoder = {}
for k in globals()[self.seq[0]].decoder.keys():
self.__decoder[k] = self
return self.__decoder
class OptionFactory(Factory):
def __init__(self, label, opts):
super().__init__()
self.label = label
self.opts = opts
self.__opcodes = None
self.__decoder = 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)
try:
if len(self.opts) == 1:
globals()[self.opts[0]].parse(context, tree)
else:
self.decoder[opcode].parse(context, tree)
return tree
except KeyError:
raise DecodeError(opcode, self.label)
def opcodes(self):
if not self.__opcodes:
self.__opcodes = []
@property
def decoder(self):
if not self.__decoder:
self.__decoder = {}
for opt in self.opts:
self.__opcodes.extend(globals()[opt].opcodes())
return self.__opcodes
self.__decoder.update(globals()[opt].decoder)
return self.__decoder
class DeferredExpansion(Transformer):
def __init__(self, context):
@ -390,6 +432,7 @@ class DeferredExpansion(Transformer):
tree.children.extend(aux_tree.children)
tree.deferred_range = None
tree.factory = None
tree.complete_parsing()
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))
@ -402,9 +445,9 @@ class DeferredExpansion(Transformer):
################################################################################
def DefAlias_hook_post(context, tree):
target = tree.children[0].children
name = tree.children[1].children
sym = AliasDecl(name, target, tree)
source = tree.SourceObject.value
alias = tree.AliasObject.value
sym = AliasDecl(alias, source, tree)
context.register_symbol(sym)
def DefName_hook_named(context, tree, name):
@ -419,32 +462,32 @@ def DefScope_hook_post(context, tree):
def DefCreateBitField_hook_named(context, tree, name):
name = tree.children[2].children
name = tree.children[2].value
sym = FieldDecl(name, 1, tree)
context.register_symbol(sym)
def DefCreateByteField_hook_named(context, tree, name):
name = tree.children[2].children
name = tree.children[2].value
sym = FieldDecl(name, 8, tree)
context.register_symbol(sym)
def DefCreateDWordField_hook_named(context, tree, name):
name = tree.children[2].children
name = tree.children[2].value
sym = FieldDecl(name, 32, tree)
context.register_symbol(sym)
def DefCreateField_hook_named(context, tree, name):
name = tree.children[3].children
name = tree.children[3].value
sym = FieldDecl(name, 0, tree)
context.register_symbol(sym)
def DefCreateQWordField_hook_named(context, tree, name):
name = tree.children[2].children
name = tree.children[2].value
sym = FieldDecl(name, 64, tree)
context.register_symbol(sym)
def DefCreateWordField_hook_named(context, tree, name):
name = tree.children[2].children
name = tree.children[2].value
sym = FieldDecl(name, 16, tree)
context.register_symbol(sym)
@ -457,9 +500,9 @@ 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
name = tree.NameString.value
ty = tree.ObjectType.value
nargs = tree.ArgumentCount.value
if ty == 0x8: # an external method
sym = MethodDecl(name, nargs, tree)
@ -479,52 +522,50 @@ access_width_map = {
def DefField_hook_post(context, tree):
# Update the fields with region & offset info
region_name = context.lookup_symbol(tree.children[1].children).name
flags = tree.children[2].children[0].children
region_name = context.lookup_symbol(tree.NameString.value).name
flags = tree.FieldFlags.value
access_width = access_width_map[flags & 0xF]
fields = tree.children[3].children
fields = tree.FieldList.FieldElements
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
name = field.NameSeg.value
length = field.FieldLength.value
sym = context.lookup_symbol(name)
assert isinstance(sym, OperationFieldDecl)
sym.set_location(region_name, bit_offset, access_width)
bit_offset += length
elif field.label == "ReservedField":
length = field.children[0].children
length = field.FieldLength.value
bit_offset += length
else:
break
def DefIndexField_hook_post(context, tree):
# Update the fields with region & offset info
index_register = context.lookup_symbol(tree.children[1].children)
data_register = context.lookup_symbol(tree.children[2].children)
flags = tree.children[3].children[0].children
index_register = context.lookup_symbol(tree.IndexName.value)
data_register = context.lookup_symbol(tree.DataName.value)
flags = tree.FieldFlags.value
access_width = access_width_map[flags & 0xF]
fields = tree.children[4].children
fields = tree.FieldList.FieldElements
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
name = field.NameSeg.value
length = field.FieldLength.value
sym = context.lookup_symbol(name)
assert isinstance(sym, OperationFieldDecl)
sym.set_indexed_location(index_register, data_register, bit_offset, access_width)
bit_offset += length
elif field.label == "ReservedField":
length = field.children[0].children
length = field.FieldLength.value
bit_offset += length
else:
break
def NamedField_hook_post(context, tree):
name = tree.children[0].children
length = tree.children[1].children
name = tree.NameSeg.value
length = tree.FieldLength.value
sym = OperationFieldDecl(name, length, tree)
context.register_symbol(sym)
@ -534,8 +575,9 @@ def DefMethod_hook_named(context, tree, 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
# Parsing of the method may be deferred. Do not use named fields to access its children.
name = tree.children[1].value
flags = tree.children[2].value
nargs = flags & 0x7
sym = MethodDecl(name, nargs, tree)
context.register_symbol(sym)

View File

@ -13,6 +13,8 @@ class Tree:
self.children = copy(children)
self.scope = None
self.structure = None
self.package_range = None
self.deferred_range = None
@ -22,6 +24,26 @@ class Tree:
def append_child(self, child):
self.children.append(child)
def register_structure(self, structure):
self.structure = structure
def complete_parsing(self):
i = 0
for elem in self.structure:
if isinstance(elem, str):
if elem.endswith("?"):
if i < len(self.children):
setattr(self, elem[:-1], self.children[i])
else:
setattr(self, elem[:-1], None)
break
elif elem.endswith("*"):
setattr(self, elem[:-1] + "s", self.children[i:])
break
else:
setattr(self, elem, self.children[i])
i += 1
class Visitor:
def __init__(self):
self.depth = 0
@ -35,7 +57,6 @@ class Visitor:
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):
@ -57,7 +78,6 @@ class Transformer:
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):
@ -66,7 +86,6 @@ class Transformer:
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):
@ -74,28 +93,6 @@ class Transformer:
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

View File

@ -13,11 +13,12 @@ class PrintLayoutVisitor(Visitor):
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 hasattr(tree, "value"):
if isinstance(tree.value, int):
print(f" = {hex(tree.value)}", end="")
elif isinstance(tree.value, str):
if self.__is_printable(tree.value):
print(f" = '{tree.value}'", end="")
if tree.deferred_range:
print(f" (deferred at {hex(tree.deferred_range[0])}, length {hex(tree.deferred_range[1])})", end="")
if tree.factory:
@ -39,7 +40,7 @@ class ConditionallyUnregisterSymbolVisitor(Visitor):
def f(tree):
if self.conditionally_hidden:
scope = tree.scope
name = tree.children[name_string_idx].children
name = tree.children[name_string_idx].value
realpath = self.context.realpath(scope, name)
self.context.unregister_object(realpath)
return f

View File

@ -8,7 +8,7 @@ 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.tree import Tree
from acpiparser.aml.context import Context
from acpiparser.aml.interpreter import ConcreteInterpreter
from acpiparser.aml.visitors import ConditionallyUnregisterSymbolVisitor
@ -29,7 +29,6 @@ def DSDT(val):
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()