board_inspector: collect inter-device dependency in board XMLs

AML allows devices defined in an ACPI namespace to have inter-dependency,
i.e. a method defined in one device can refer to objects in other
devices. While such inter-dependency is common in device manipulation
methods, device identification and configuration methods, such as _CRS, may
depend on other devices as well.

An example we have already met is a PCS (Physical Coding Sublayer) which
calculates resource descriptors by accessing the PCI configuration space of
the accompanying Ethernet controller. Without the ACPI object describing
the PCS, a driver of the Ethernet controller may refuse to initialize.

This patch adds a preliminary dependency analyzer to detect such
inter-device dependency. The analyzer walks through the reference chains of
an object, identifying whether the referenced objects are operation fields
of a device. Depending on the result of this analysis, the board XML is
refined as follows.

  * When an object (probably a method) references such fields, the original
    object definition in host DSDT/SSDTs will be copied in the AML template
    so that they still work in VMs where the operation fields may be
    virtualized. Such objects will be referred to as "copied objects"
    hereinafter.

  * The objects that are **directly** referenced by a copied object is
    added in the AML template as well. Such objects still belong to devices
    where they are originally defined in the host ACPI namespace. Their
    definition, however, may be copied or replaced with constant values,
    depending on the dependency analysis on these objects.

  * Nodes with the "dependency" tag are added under "device" nodes in the
    board XML, allowing the configuration tools to follow the device
    dependency chain when generating vACPI tables. These nodes only
    represent direct dependencies; indirect dependencies can be inferred by
    following those direct ones.

The current implementation does not allow objects being added to AML
templates if they refer to any of the following.

  * Global objects, i.e. objects not belonging to any device. Such objects
    tend to encode system-wide information, such as the ACPI
    NVS (Non-Volatile Storage) or its fields.

  * Methods with parameters.

Objects with such references are thus being hidden from guest software,
just like how they are invisible in the current implementation.

This patch is added in v2 of the series.

v2 -> v3:
  * Also collect dependencies due to providing or consuming resources.
  * Refactor the dependency detection logic for clarity.

Tracked-On: #6287
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
Junjie Mao
2021-08-02 16:13:50 +08:00
committed by wenlingz
parent a2e9a05737
commit d6e47bcea5
4 changed files with 307 additions and 21 deletions

View File

@@ -30,6 +30,14 @@ class FieldDecl(NamedDecl):
def dump(self):
print(f"{self.name}: {self.__class__.__name__}, {self.length} bits")
class OperationRegionDecl(NamedDecl):
@staticmethod
def object_type():
return 10
def __init__(self, name, tree):
super().__init__(name, tree)
class OperationFieldDecl(NamedDecl):
def __init__(self, name, length, tree):
super().__init__(name, tree)
@@ -37,6 +45,7 @@ class OperationFieldDecl(NamedDecl):
self.offset = None
self.length = length
self.access_width = None
self.parent_tree = None
def set_location(self, region, offset, access_width):
self.region = region
@@ -155,6 +164,13 @@ class Context:
else:
return parent
@staticmethod
def normalize_namepath(namepath):
path = namepath.lstrip("\\^")
prefix = namepath[:(len(namepath) - len(path))]
parts = '.'.join(map(lambda x: x[:4].ljust(4, '_'), path.split(".")))
return prefix + parts
def __init__(self):
self.streams = {}
self.current_stream = None
@@ -266,18 +282,25 @@ class Context:
prefix_len -= 1
raise KeyError(name)
def lookup_symbol(self, name):
def lookup_symbol(self, name, scope=None):
if scope:
self.change_scope(scope)
try:
if name.startswith("\\"):
return self.__symbol_table[name]
ret = self.__symbol_table[name]
elif name.startswith("^") or name.find(".") >= 0:
realpath = self.realpath(self.__current_scope, name)
return self.__symbol_table[realpath]
ret = self.__symbol_table[realpath]
else:
return self.__lookup_symbol_in_parents(self.__symbol_table, name)
ret = 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())
ret = None
if scope:
self.pop_scope()
if not ret:
raise UndefinedSymbol(name, scope if scope else self.get_scope())
return ret
def has_symbol(self, name):
try:

View File

@@ -531,6 +531,7 @@ def DefField_hook_post(context, tree):
sym = context.lookup_symbol(name)
assert isinstance(sym, OperationFieldDecl)
sym.set_location(region_name, bit_offset, access_width)
sym.parent_tree = tree
bit_offset += length
elif field.label == "ReservedField":
length = field.FieldLength.value
@@ -580,7 +581,7 @@ def DefMethod_hook_post(context, tree):
context.register_symbol(sym)
def DefOpRegion_hook_named(context, tree, name):
sym = NamedDecl(name, tree)
sym = OperationRegionDecl(name, tree)
context.register_symbol(sym)
def DefPowerRes_hook_named(context, tree, name):