summaryrefslogtreecommitdiffstats
path: root/DynamicTablesPkg
diff options
context:
space:
mode:
Diffstat (limited to 'DynamicTablesPkg')
-rw-r--r--DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c1458
-rw-r--r--DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h188
2 files changed, 1646 insertions, 0 deletions
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c
new file mode 100644
index 0000000000..bc98d950b5
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c
@@ -0,0 +1,1458 @@
+/** @file
+ AML Method Parser.
+
+ Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Parser/AmlMethodParser.h>
+
+#include <AmlCoreInterface.h>
+#include <AmlDbgPrint/AmlDbgPrint.h>
+#include <NameSpace/AmlNameSpace.h>
+#include <Parser/AmlParser.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTree.h>
+#include <String/AmlString.h>
+
+/** Delete a namespace reference node and its pathname.
+
+ It is the caller's responsibility to check the NameSpaceRefNode has been
+ removed from any list the node is part of.
+
+ @param [in] NameSpaceRefNode Pointer to an AML_NAMESPACE_REF_NODE.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlDeleteNameSpaceRefNode (
+ IN AML_NAMESPACE_REF_NODE * NameSpaceRefNode
+ )
+{
+ if (NameSpaceRefNode == NULL) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NameSpaceRefNode->RawAbsolutePath != NULL) {
+ FreePool ((CHAR8*)NameSpaceRefNode->RawAbsolutePath);
+ } else {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (NameSpaceRefNode);
+ return EFI_SUCCESS;
+}
+
+/** Delete a list of namespace reference nodes.
+
+ @param [in] NameSpaceRefList List of namespace reference nodes.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlDeleteNameSpaceRefList (
+ IN LIST_ENTRY * NameSpaceRefList
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY * CurrentLink;
+
+ if (NameSpaceRefList == NULL) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ while (!IsListEmpty (NameSpaceRefList)) {
+ CurrentLink = NameSpaceRefList->ForwardLink;
+ RemoveEntryList (CurrentLink);
+ Status = AmlDeleteNameSpaceRefNode (
+ (AML_NAMESPACE_REF_NODE*)CurrentLink
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+ } // while
+
+ return EFI_SUCCESS;
+}
+
+/** Create an AML_NAMESPACE_REF_NODE.
+
+ A Buffer is allocated to store the raw AML absolute path.
+
+ @param [in] ObjectNode Node being part of the namespace.
+ Must be have the AML_IN_NAMESPACE
+ attribute.
+ @param [in] RawAbsolutePath AML raw absolute path of the ObjectNode.
+ A raw NameString is a concatenated list
+ of 4 chars long names.
+ @param [in] RawAbsolutePathSize Size of the RawAbsolutePath buffer.
+ @param [out] NameSpaceRefNodePtr The created AML_METHOD_REF_NODE.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlCreateMethodRefNode (
+ IN CONST AML_OBJECT_NODE * ObjectNode,
+ IN CONST CHAR8 * RawAbsolutePath,
+ IN UINT32 RawAbsolutePathSize,
+ OUT AML_NAMESPACE_REF_NODE ** NameSpaceRefNodePtr
+ )
+{
+ AML_NAMESPACE_REF_NODE * NameSpaceRefNode;
+
+ if (!AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE) ||
+ (RawAbsolutePath == NULL) ||
+ (RawAbsolutePathSize == 0) ||
+ (NameSpaceRefNodePtr == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NameSpaceRefNode = AllocateZeroPool (sizeof (AML_NAMESPACE_REF_NODE));
+ if (NameSpaceRefNode == NULL) {
+ ASSERT (0);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NameSpaceRefNode->RawAbsolutePathSize = RawAbsolutePathSize;
+ NameSpaceRefNode->RawAbsolutePath = AllocateCopyPool (
+ RawAbsolutePathSize,
+ RawAbsolutePath
+ );
+ if (NameSpaceRefNode->RawAbsolutePath == NULL) {
+ FreePool (NameSpaceRefNode);
+ ASSERT (0);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (&NameSpaceRefNode->Link);
+
+ NameSpaceRefNode->NodeRef = ObjectNode;
+ *NameSpaceRefNodePtr = NameSpaceRefNode;
+
+ return EFI_SUCCESS;
+}
+
+#if !defined (MDEPKG_NDEBUG)
+
+/** Print the list of raw absolute paths of the NameSpace reference list.
+
+ @param [in] NameSpaceRefList List of NameSpace reference nodes.
+**/
+VOID
+EFIAPI
+AmlDbgPrintNameSpaceRefList (
+ IN CONST LIST_ENTRY * NameSpaceRefList
+ )
+{
+ LIST_ENTRY * CurrLink;
+ AML_NAMESPACE_REF_NODE * CurrNameSpaceNode;
+
+ if (NameSpaceRefList == NULL) {
+ ASSERT (0);
+ return;
+ }
+
+ DEBUG ((DEBUG_INFO, "AmlMethodParser: List of available raw AML paths:\n"));
+
+ CurrLink = NameSpaceRefList->ForwardLink;
+ while (CurrLink != NameSpaceRefList) {
+ CurrNameSpaceNode = (AML_NAMESPACE_REF_NODE*)CurrLink;
+
+ AmlDbgPrintChars (
+ DEBUG_INFO,
+ CurrNameSpaceNode->RawAbsolutePath,
+ CurrNameSpaceNode->RawAbsolutePathSize
+ );
+ DEBUG ((DEBUG_INFO, "\n"));
+
+ CurrLink = CurrLink->ForwardLink;
+ }
+
+ DEBUG ((DEBUG_INFO, "\n"));
+}
+
+#endif // MDEPKG_NDEBUG
+
+/** From a forward stream pointing to a NameString,
+ initialize a raw backward stream.
+
+ StartOfStream
+ Fstream: CurrPos EndOfStream
+ v v
+ +-----------------------------------------+
+ |^^^[Multi-name prefix]AAAA.BBBB.CCCC |
+ +-----------------------------------------+
+ ^ ^
+ RawPathNameBStream: EndOfStream CurrPos
+ StartOfStream
+
+ No memory is allocated when initializing the stream.
+
+ @param [in] FStream Forward stream pointing to a NameString.
+ The stream must not be at its end.
+ @param [out] RawPathNameBStream Backward stream containing the
+ raw AML path.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlInitRawPathBStream (
+ IN CONST AML_STREAM * FStream,
+ OUT AML_STREAM * RawPathNameBStream
+ )
+{
+ EFI_STATUS Status;
+
+ UINT8 * RawPathBuffer;
+ CONST CHAR8 * Buffer;
+
+ UINT32 Root;
+ UINT32 ParentPrefix;
+ UINT32 SegCount;
+
+ if (!IS_STREAM (FStream) ||
+ IS_END_OF_STREAM (FStream) ||
+ !IS_STREAM_FORWARD (FStream) ||
+ (RawPathNameBStream == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Buffer = (CONST CHAR8*)AmlStreamGetCurrPos (FStream);
+ if (Buffer == NULL) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Parse the NameString information.
+ Status = AmlParseNameStringInfo (
+ Buffer,
+ &Root,
+ &ParentPrefix,
+ &SegCount
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Get the beginning of the raw NameString.
+ RawPathBuffer = (UINT8*)AmlGetFirstNameSeg (
+ Buffer,
+ Root,
+ ParentPrefix
+ );
+ if (RawPathBuffer == NULL) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Initialize a backward stream containing the raw path.
+ Status = AmlStreamInit (
+ RawPathNameBStream,
+ RawPathBuffer,
+ (SegCount * AML_NAME_SEG_SIZE),
+ EAmlStreamDirectionBackward
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
+
+/** Get the first node in the ParentNode branch that is part of the
+ AML namespace and has its name defined.
+
+ This is different from getting the first namespace node. This function is
+ necessary because an absolute path is built while the tree is not complete
+ yet. The parsing is ongoing.
+
+ For instance, the ASL statement "CreateXXXField ()" adds a field in the
+ AML namespace, but the name it defines is the last fixed argument of the
+ corresponding object.
+ If an AML path is referenced in its first fixed argument, it is not
+ possible to resolve the name of the CreateXXXField object. However, the AML
+ path is not part of the scope created by the CreateXXXField object, so this
+ scope can be skipped.
+
+ In the following ASL code, the method invocation to MET0 is done in the
+ "CreateField" statement. The "CreateField" statement defines the "FIEL"
+ path in the AML namespace. However, MET0 must be not be resolved in the
+ "CreateField" object scope. It needs to be resolved in its parent.
+ ASL code:
+ Method (MET0, 0,,, BuffObj) {
+ Return (Buffer (0x1000) {})
+ }
+ CreateField (MET0(), 0x100, 0x4, FIEL)
+
+ @param [in] Node Node to get the first named node from, in
+ its hierarchy.
+ @param [out] OutNamedNode First named node in Node's hierarchy.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlGetFirstNamedAncestorNode (
+ IN CONST AML_NODE_HEADER * Node,
+ OUT AML_NODE_HEADER ** OutNamedNode
+ )
+{
+ EFI_STATUS Status;
+ CONST AML_NODE_HEADER * NameSpaceNode;
+
+ if ((!IS_AML_OBJECT_NODE (Node) &&
+ !IS_AML_ROOT_NODE (Node)) ||
+ (OutNamedNode == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // If Node is not the root node and doesn't have a name defined yet,
+ // get the ancestor NameSpace node.
+ while (!IS_AML_ROOT_NODE (Node) &&
+ !(AmlNodeHasAttribute (
+ (CONST AML_OBJECT_NODE*)Node,
+ AML_IN_NAMESPACE) &&
+ AmlNodeGetName ((CONST AML_OBJECT_NODE*)Node) != NULL)) {
+ Status = AmlGetFirstAncestorNameSpaceNode (
+ Node,
+ (AML_NODE_HEADER**)&NameSpaceNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+ // The NameSpaceNode may not have its name defined as yet. In this
+ // case get the next ancestor node.
+ Node = NameSpaceNode;
+ }
+
+ *OutNamedNode = (AML_NODE_HEADER*)Node;
+
+ return EFI_SUCCESS;
+}
+
+/** From a ParentNode and a forward stream pointing to a relative path,
+ build a raw AML absolute path and return it in a backward stream.
+
+ No memory is allocated in this function, the out stream must be initialized
+ with a buffer long enough to hold any raw absolute AML path.
+
+ @param [in] ParentNode Parent node of the namespace
+ node from which the absolute
+ path is built. ParentNode isn't
+ necessarily a namespace node.
+ Must be a root or an object node.
+ @param [in] PathnameFStream Forward stream pointing to the
+ beginning of a pathname (any
+ NameString).
+ The stream must not be at its end.
+ @param [in, out] AbsolutePathBStream Backward stream where the raw
+ absolute path is written. The
+ stream must be already initialized.
+ The stream must not be at its end.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_BUFFER_TOO_SMALL No space left in the buffer.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlBuildRawMethodAbsolutePath (
+ IN CONST AML_NODE_HEADER * ParentNode,
+ IN CONST AML_STREAM * PathnameFStream,
+ IN OUT AML_STREAM * AbsolutePathBStream
+ )
+{
+ EFI_STATUS Status;
+
+ AML_NODE_HEADER * NamedParentNode;
+ UINT8 * RawPathBuffer;
+ CONST CHAR8 * CurrPos;
+
+ UINT32 Root;
+ UINT32 ParentPrefix;
+ UINT32 SegCount;
+
+ if ((!IS_AML_OBJECT_NODE (ParentNode) &&
+ !IS_AML_ROOT_NODE (ParentNode)) ||
+ !IS_STREAM (PathnameFStream) ||
+ IS_END_OF_STREAM (PathnameFStream) ||
+ !IS_STREAM_FORWARD (PathnameFStream) ||
+ !IS_STREAM (AbsolutePathBStream) ||
+ IS_END_OF_STREAM (AbsolutePathBStream) ||
+ !IS_STREAM_BACKWARD (AbsolutePathBStream)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CurrPos = (CONST CHAR8*)AmlStreamGetCurrPos (PathnameFStream);
+ if (CurrPos == NULL) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Parse the NameString information.
+ Status = AmlParseNameStringInfo (
+ CurrPos,
+ &Root,
+ &ParentPrefix,
+ &SegCount
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Copy the method invocation raw relative path at the end of the Stream.
+ if (SegCount != 0) {
+ // Get the beginning of the raw NameString.
+ RawPathBuffer = (UINT8*)AmlGetFirstNameSeg (
+ CurrPos,
+ Root,
+ ParentPrefix
+ );
+
+ Status = AmlStreamWrite (
+ AbsolutePathBStream,
+ RawPathBuffer,
+ SegCount * AML_NAME_SEG_SIZE
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+ }
+
+ // If the pathname contained an absolute path, this is finished, return.
+ if (Root) {
+ return Status;
+ }
+
+ // Get the first named node of the parent node in its hierarchy.
+ Status = AmlGetFirstNamedAncestorNode (ParentNode, &NamedParentNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Build the raw absolute path of the namespace node.
+ Status = AmlGetRawNameSpacePath (
+ NamedParentNode,
+ ParentPrefix,
+ AbsolutePathBStream
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
+
+/** Compare two raw NameStrings stored in forward streams.
+ Compare them NameSeg by NameSeg (a NameSeg is 4 bytes long).
+
+ The two raw NameStrings can be of different size.
+
+ @param [in] RawFStream1 First forward stream to compare.
+ Points to the beginning of the raw NameString.
+ @param [in] RawFStream2 Second forward stream to compare.
+ Points to the beginning of the raw NameString.
+ @param [out] CompareCount Count of identic bytes.
+ Must be a multiple of 4 (size of a NameSeg).
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlCompareRawNameString (
+ IN CONST AML_STREAM * RawFStream1,
+ IN CONST AML_STREAM * RawFStream2,
+ OUT UINT32 * CompareCount
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+
+ AML_STREAM RawFStream1Clone;
+ AML_STREAM RawFStream2Clone;
+ UINT32 Stream1Size;
+ UINT32 Stream2Size;
+ UINT32 CompareLen;
+
+ // Raw NameStrings have a size that is a multiple of the size of NameSegs.
+ if (!IS_STREAM (RawFStream1) ||
+ IS_END_OF_STREAM (RawFStream1) ||
+ !IS_STREAM_FORWARD (RawFStream1) ||
+ !IS_STREAM (RawFStream2) ||
+ IS_END_OF_STREAM (RawFStream2) ||
+ (CompareCount == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Stream1Size = AmlStreamGetFreeSpace (RawFStream1);
+ if ((Stream1Size & (AML_NAME_SEG_SIZE - 1)) != 0) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Stream2Size = AmlStreamGetFreeSpace (RawFStream2);
+ if ((Stream2Size & (AML_NAME_SEG_SIZE - 1)) != 0) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = AmlStreamClone (RawFStream1, &RawFStream1Clone);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlStreamClone (RawFStream2, &RawFStream2Clone);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ CompareLen = MIN (Stream1Size, Stream2Size);
+ Index = 0;
+ // Check there is enough space for a NameSeg in both Stream1 and Stream2.
+ while (Index < CompareLen) {
+ if (!AmlStreamCmp (
+ &RawFStream1Clone,
+ &RawFStream2Clone,
+ AML_NAME_SEG_SIZE)
+ ) {
+ // NameSegs are different. Break.
+ break;
+ }
+
+ Status = AmlStreamProgress (&RawFStream1Clone, AML_NAME_SEG_SIZE);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+ Status = AmlStreamProgress (&RawFStream2Clone, AML_NAME_SEG_SIZE);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Index += AML_NAME_SEG_SIZE;
+ }
+
+ *CompareCount = Index;
+
+ return EFI_SUCCESS;
+}
+
+/** Check whether an alias can be resolved to a method definition.
+
+ Indeed, the following ASL code must be handled:
+ Method (MET0, 1) {
+ Return (0x9)
+ }
+ Alias (\MET0, \ALI0)
+ Alias (\ALI0, \ALI1)
+ \ALI1(0x5)
+ When searching for \ALI1 in the AML NameSpace, it resolves to \ALI0.
+ When searching for \ALI0 in the AML NameSpace, it resolves to \MET0.
+ When searching for \MET0 in the AML NameSpace, it resolves to a method
+ definition.
+
+ This method is a wrapper to recursively call AmlFindMethodDefinition.
+
+ @param [in] AliasNode Pointer to an Alias object node.
+ @param [in] NameSpaceRefList List of NameSpaceRef nodes.
+ @param [out] OutNameSpaceRefNode If success, and if the alias is resolved
+ to a method definition (this can go
+ through other alias indirections),
+ containing the corresponding
+ NameSpaceRef node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlResolveAliasMethod (
+ IN CONST AML_OBJECT_NODE * AliasNode,
+ IN CONST LIST_ENTRY * NameSpaceRefList,
+ OUT AML_NAMESPACE_REF_NODE ** OutNameSpaceRefNode
+ )
+{
+ EFI_STATUS Status;
+ AML_STREAM SourceAliasFStream;
+ CONST AML_DATA_NODE * DataNode;
+
+ if (!AmlNodeCompareOpCode (AliasNode, AML_ALIAS_OP, 0) ||
+ (NameSpaceRefList == NULL) ||
+ (OutNameSpaceRefNode == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // The aliased NameString (the source name) is the first fixed argument,
+ // cf. ACPI6.3 spec, s19.6.4: Alias (SourceObject, AliasObject)
+ DataNode = (CONST AML_DATA_NODE*)AmlGetFixedArgument (
+ (AML_OBJECT_NODE*)AliasNode,
+ EAmlParseIndexTerm0
+ );
+ if (DataNode == NULL) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Initialize a stream on the source alias NameString.
+ Status = AmlStreamInit (
+ &SourceAliasFStream,
+ DataNode->Buffer,
+ DataNode->Size,
+ EAmlStreamDirectionForward
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Recursively check whether the source alias NameString
+ // is a method invocation.
+ Status = AmlIsMethodInvocation (
+ AmlGetParent ((AML_NODE_HEADER*)AliasNode),
+ &SourceAliasFStream,
+ NameSpaceRefList,
+ OutNameSpaceRefNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ }
+
+ return Status;
+}
+
+/** Iterate through the MethodList to find the best namespace resolution.
+ If the pathname resolves to a method definition, returns it.
+
+ For instance, if the AML namespace is:
+ \
+ \-MET0 <- Device definition, absolute path: \MET0
+ \-AAAA
+ \-MET0 <- Method definition, absolute path: \AAAA.MET0
+ \-MET1 <- Method definition, absolute path: \AAAA.MET1
+ \-BBBB
+ \-CCCC
+ \-DDDD
+ \-MET0 <- Method definition, absolute path: \AAAA.BBBB.CCCC.DDDD.MET0
+
+ The list of the available pathnames is:
+ [NameSpaceRefList]
+ - \MET0 <- Device definition
+ - \AAAA
+ - \AAAA.MET0 <- Method definition
+ - \AAAA.MET1 <- Method definition
+ - \AAAA.BBBB
+ - \AAAA.BBBB.CCCC
+ - \AAAA.BBBB.CCCC.DDDD
+ - \AAAA.BBBB.CCCC.DDDD.MET0 <- Method definition
+
+ Depending on where the method invocation is done, the method definition
+ referenced changes. If the method call "MET0" is done from
+ \AAAA.BBBB.CCCC:
+ 1. Identify which pathnames end with "MET0":
+ - \MET0 <- Device definition
+ - \AAAA.MET0 <- Method definition
+ - \AAAA.BBBB.CCCC.DDDD.MET0 <- Method definition
+ 2. Resolve the method invocation:
+ - \AAAA.MET0 <- Method definition
+ 3. \AAAA.MET0 is a method definition, so return the corresponding
+ reference node.
+
+ @param [in] RawAbsolutePathFStream Forward stream pointing to a raw
+ absolute path.
+ The stream must not be at its end.
+ @param [in] RawPathNameBStream Backward stream pointing to a raw
+ pathname. This raw pathname is the
+ raw NameString of namespace node.
+ The stream must not be at its end.
+ @param [in] NameSpaceRefList List of NameSpaceRef nodes.
+ @param [out] OutNameSpaceRefNode If the two input paths are
+ referencing a method definition,
+ returns the corresponding
+ NameSpaceRef node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlFindMethodDefinition (
+ IN CONST AML_STREAM * RawAbsolutePathFStream,
+ IN CONST AML_STREAM * RawPathNameBStream,
+ IN CONST LIST_ENTRY * NameSpaceRefList,
+ OUT AML_NAMESPACE_REF_NODE ** OutNameSpaceRefNode
+ )
+{
+ EFI_STATUS Status;
+
+ LIST_ENTRY * NextLink;
+
+ // To resolve a pathname, scope levels need to be compared.
+ UINT32 NameSegScopeCount;
+ UINT32 PathNameSegScopeCount;
+ UINT32 ProbedScopeCount;
+ UINT32 BestScopeCount;
+
+ AML_STREAM ProbedRawAbsoluteFStream;
+ AML_STREAM ProbedRawAbsoluteBStream;
+
+ AML_NAMESPACE_REF_NODE * ProbedNameSpaceRefNode;
+ AML_NAMESPACE_REF_NODE * BestNameSpaceRefNode;
+
+ if (!IS_STREAM (RawAbsolutePathFStream) ||
+ IS_END_OF_STREAM (RawAbsolutePathFStream) ||
+ !IS_STREAM_FORWARD (RawAbsolutePathFStream) ||
+ ((AmlStreamGetIndex (RawAbsolutePathFStream) &
+ (AML_NAME_SEG_SIZE - 1)) != 0) ||
+ !IS_STREAM (RawPathNameBStream) ||
+ IS_END_OF_STREAM (RawPathNameBStream) ||
+ !IS_STREAM_BACKWARD (RawPathNameBStream) ||
+ ((AmlStreamGetIndex (RawPathNameBStream) &
+ (AML_NAME_SEG_SIZE - 1)) != 0) ||
+ (NameSpaceRefList == NULL) ||
+ (OutNameSpaceRefNode == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "AmlMethodParser: Checking absolute name: "));
+ AmlDbgPrintChars (
+ DEBUG_VERBOSE,
+ (CONST CHAR8*)AmlStreamGetCurrPos (RawAbsolutePathFStream),
+ AmlStreamGetMaxBufferSize (RawAbsolutePathFStream)
+ );
+ DEBUG ((DEBUG_VERBOSE, ".\n"));
+
+ BestNameSpaceRefNode = NULL;
+ BestScopeCount = 0;
+ NameSegScopeCount = AmlStreamGetMaxBufferSize (RawAbsolutePathFStream);
+ PathNameSegScopeCount = AmlStreamGetMaxBufferSize (RawPathNameBStream);
+
+ // Iterate through the raw AML absolute path to find the best match.
+ DEBUG ((DEBUG_VERBOSE, "AmlMethodParser: Comparing with: "));
+ NextLink = NameSpaceRefList->ForwardLink;
+ while (NextLink != NameSpaceRefList) {
+ ProbedNameSpaceRefNode = (AML_NAMESPACE_REF_NODE*)NextLink;
+
+ // Print the raw absolute path of the probed node.
+ AmlDbgPrintChars (
+ DEBUG_VERBOSE,
+ ProbedNameSpaceRefNode->RawAbsolutePath,
+ ProbedNameSpaceRefNode->RawAbsolutePathSize
+ );
+ DEBUG ((DEBUG_VERBOSE, "; "));
+
+ // If the raw AML absolute path of the probed node is longer than the
+ // searched pathname, continue.
+ // E.g.: The method call \MET0 cannot resolve to a method defined at
+ // \AAAA.MET0. The method definition is out of scope.
+ if (PathNameSegScopeCount > ProbedNameSpaceRefNode->RawAbsolutePathSize) {
+ NextLink = NextLink->ForwardLink;
+ continue;
+ }
+
+ // Initialize a backward stream for the probed node.
+ // This stream is used to compare the ending of the pathnames.
+ // E.g. if the method searched ends with "MET0", pathnames not ending with
+ // "MET0" should be skipped.
+ Status = AmlStreamInit (
+ &ProbedRawAbsoluteBStream,
+ (UINT8*)ProbedNameSpaceRefNode->RawAbsolutePath,
+ ProbedNameSpaceRefNode->RawAbsolutePathSize,
+ EAmlStreamDirectionBackward
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Compare the pathname endings. If they don't match, continue.
+ if (!AmlStreamCmp (
+ RawPathNameBStream,
+ &ProbedRawAbsoluteBStream,
+ AmlStreamGetMaxBufferSize (RawPathNameBStream))) {
+ NextLink = NextLink->ForwardLink;
+ continue;
+ }
+
+ // Initialize a forward stream for the probed node.
+ // This stream is used to count how many scope levels from the root
+ // are common with the probed node. The more there are, the better it is.
+ // E.g.: For the method invocation \AAAA.BBBB.MET0, if there are 2
+ // pathnames ending with MET0:
+ // - \AAAA.MET0 has 1 NameSeg in common with \AAAA.BBBB.MET0
+ // from the root (this is "AAAA");
+ // - \MET0 has 0 NameSeg in common with \AAAA.BBBB.MET0
+ // from the root;
+ // Thus, the best match is \AAAA.MET0.
+ Status = AmlStreamInit (
+ &ProbedRawAbsoluteFStream,
+ (UINT8*)ProbedNameSpaceRefNode->RawAbsolutePath,
+ ProbedNameSpaceRefNode->RawAbsolutePathSize,
+ EAmlStreamDirectionForward
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Count how many namespace levels are in common from the root.
+ Status = AmlCompareRawNameString (
+ RawAbsolutePathFStream,
+ &ProbedRawAbsoluteFStream,
+ &ProbedScopeCount
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (ProbedScopeCount == NameSegScopeCount) {
+ // This is a perfect match. Exit the loop.
+ BestNameSpaceRefNode = ProbedNameSpaceRefNode;
+ break;
+ } else if (ProbedScopeCount > BestScopeCount) {
+ // The probed node has more scope levels in common than the
+ // last best match. Update the best match.
+ BestScopeCount = ProbedScopeCount;
+ BestNameSpaceRefNode = ProbedNameSpaceRefNode;
+ } else if (ProbedScopeCount == BestScopeCount) {
+ // The probed node has the same number of scope levels in
+ // common as the last best match.
+ if (ProbedScopeCount == 0) {
+ // There was not best match previously. Set it.
+ BestNameSpaceRefNode = ProbedNameSpaceRefNode;
+ } else {
+ // (ProbedScopeCount != 0)
+ // If there is an equivalent candidate, the best has the shortest
+ // absolute path. Indeed, a similar ProbedScopeCount and a longer
+ // path means the definition is out of the scope.
+ // E.g.: For the method invocation \AAAA.BBBB.MET0, if there are 2
+ // pathnames ending with MET0:
+ // - \AAAA.MET0 has 1 NameSegs in common with \AAAA.BBBB.MET0
+ // from the root (this is "AAAA");
+ // - \AAAA.CCCC.MET0 has 1 NameSegs in common with
+ // \AAAA.BBBB.MET0 from the root (this is "AAAA");
+ // As \AAAA.CCCC.MET0 is longer than \AAAA.MET0, it means that
+ // the pathname could have matched on more NameSegs, but it
+ // didn't because it is out of scope.
+ // Thus, the best match is \AAAA.MET0.
+ if (AmlStreamGetIndex (&ProbedRawAbsoluteFStream) <
+ BestNameSpaceRefNode->RawAbsolutePathSize) {
+ BestScopeCount = ProbedScopeCount;
+ BestNameSpaceRefNode = ProbedNameSpaceRefNode;
+ } else if (AmlStreamGetIndex (&ProbedRawAbsoluteFStream) ==
+ BestNameSpaceRefNode->RawAbsolutePathSize) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ NextLink = NextLink->ForwardLink;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "\n"));
+
+ // Check whether the BestNameSpaceRefNode is a method definition.
+ if (BestNameSpaceRefNode != NULL) {
+ if (AmlIsMethodDefinitionNode (BestNameSpaceRefNode->NodeRef)) {
+ *OutNameSpaceRefNode = BestNameSpaceRefNode;
+ } else if (AmlNodeCompareOpCode (
+ BestNameSpaceRefNode->NodeRef,
+ AML_ALIAS_OP, 0)) {
+ // The path matches an alias. Resolve the alias and check whether
+ // this is a method defintion.
+ Status = AmlResolveAliasMethod (
+ BestNameSpaceRefNode->NodeRef,
+ NameSpaceRefList,
+ OutNameSpaceRefNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+ }
+ } else {
+ // If no, return NULL, even if a matching pathname has been found.
+ *OutNameSpaceRefNode = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Check whether a pathname is a method invocation.
+
+ If there is a matching method definition, returns the corresponding
+ NameSpaceRef node.
+
+ To do so, the NameSpaceRefList is keeping track of every namespace node
+ and its raw AML absolute path.
+ To check whether a pathname is a method invocation, a corresponding raw
+ absolute pathname is built. This raw absolute pathname is then compared
+ to the list of available pathnames. If a pathname defining a method
+ matches the scope of the input pathname, return.
+
+ @param [in] ParentNode Parent node. Node to which the node to be
+ created will be attached.
+ @param [in] FStream Forward stream pointing to the NameString
+ to find.
+ @param [in] NameSpaceRefList List of NameSpaceRef nodes.
+ @param [out] OutNameSpaceRefNode If the NameString pointed by FStream is
+ a method invocation, OutNameSpaceRefNode
+ contains the NameSpaceRef corresponding
+ to the method definition.
+ NULL otherwise.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlIsMethodInvocation (
+ IN CONST AML_NODE_HEADER * ParentNode,
+ IN CONST AML_STREAM * FStream,
+ IN CONST LIST_ENTRY * NameSpaceRefList,
+ OUT AML_NAMESPACE_REF_NODE ** OutNameSpaceRefNode
+ )
+{
+ EFI_STATUS Status;
+
+ AML_STREAM RawPathNameBStream;
+ AML_STREAM RawAbsolutePathFStream;
+
+ AML_STREAM RawAbsolutePathBStream;
+ UINT8 * RawAbsolutePathBuffer;
+ UINT32 RawAbsolutePathBufferSize;
+
+ AML_NAMESPACE_REF_NODE * NameSpaceRefNode;
+
+ if ((!IS_AML_OBJECT_NODE (ParentNode) &&
+ !IS_AML_ROOT_NODE (ParentNode)) ||
+ !IS_STREAM (FStream) ||
+ IS_END_OF_STREAM (FStream) ||
+ !IS_STREAM_FORWARD (FStream) ||
+ (NameSpaceRefList == NULL) ||
+ (OutNameSpaceRefNode == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // There cannot be a method invocation in a field list. Return.
+ if (AmlNodeHasAttribute (
+ (CONST AML_OBJECT_NODE*)ParentNode,
+ AML_HAS_FIELD_LIST)) {
+ *OutNameSpaceRefNode = NULL;
+ return EFI_SUCCESS;
+ }
+
+ // Allocate memory for the raw absolute path.
+ RawAbsolutePathBufferSize = MAX_AML_NAMESTRING_SIZE;
+ RawAbsolutePathBuffer = AllocateZeroPool (RawAbsolutePathBufferSize);
+ if (RawAbsolutePathBuffer == NULL) {
+ ASSERT (0);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ // Initialize a backward stream to get the raw absolute path.
+ Status = AmlStreamInit (
+ &RawAbsolutePathBStream,
+ RawAbsolutePathBuffer,
+ RawAbsolutePathBufferSize,
+ EAmlStreamDirectionBackward
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler;
+ }
+
+ // Build the raw AML absolute path of the namespace node.
+ Status = AmlBuildRawMethodAbsolutePath (
+ ParentNode,
+ FStream,
+ &RawAbsolutePathBStream
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler;
+ }
+
+ // If this is the root path: it cannot be a method invocation. Just return.
+ if (AmlStreamGetIndex (&RawAbsolutePathBStream) == 0) {
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "AmlMethodParser: "
+ "Root node cannot be a method invocation\n"
+ ));
+ *OutNameSpaceRefNode = NULL;
+ Status = EFI_SUCCESS;
+ goto exit_handler;
+ }
+
+ // Create a forward stream for the raw absolute path.
+ // This forward stream only contains the raw absolute path with
+ // no extra free space.
+ Status = AmlStreamInit (
+ &RawAbsolutePathFStream,
+ AmlStreamGetCurrPos (&RawAbsolutePathBStream),
+ AmlStreamGetIndex (&RawAbsolutePathBStream),
+ EAmlStreamDirectionForward
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler;
+ }
+
+ // Create a backward stream for the node name.
+ Status = AmlInitRawPathBStream (
+ FStream,
+ &RawPathNameBStream
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Go through the NameSpaceRefList elements to check for
+ // a corresponding method definition.
+ NameSpaceRefNode = NULL;
+ Status = AmlFindMethodDefinition (
+ &RawAbsolutePathFStream,
+ &RawPathNameBStream,
+ NameSpaceRefList,
+ &NameSpaceRefNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler;
+ }
+
+#if !defined(MDEPKG_NDEBUG)
+ // Print whether a method definition has been found.
+ if (NameSpaceRefNode != NULL) {
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "AmlMethodParser: Corresponding method definition: "
+ ));
+ AmlDbgPrintChars (
+ DEBUG_VERBOSE,
+ NameSpaceRefNode->RawAbsolutePath,
+ NameSpaceRefNode->RawAbsolutePathSize
+ );
+ DEBUG ((DEBUG_VERBOSE, ".\n"));
+
+ } else {
+ DEBUG ((DEBUG_VERBOSE, "AmlMethodParser: No method definition found.\n"));
+ }
+#endif // MDEPKG_NDEBUG
+
+ *OutNameSpaceRefNode = NameSpaceRefNode;
+
+exit_handler:
+ // Free allocated memory.
+ FreePool (RawAbsolutePathBuffer);
+ return Status;
+}
+
+/** Create a namespace reference node and add it to the NameSpaceRefList.
+
+ When a namespace node is encountered, the namespace it defines must be
+ associated to the node. This allow to keep track of the nature of each
+ name present in the AML namespace.
+
+ In the end, this allows to recognize method invocations and parse the right
+ number of arguments after the method name.
+
+ @param [in] Node Namespace node.
+ @param [in, out] NameSpaceRefList List of namespace reference nodes.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlAddNameSpaceReference (
+ IN CONST AML_OBJECT_NODE * Node,
+ IN OUT LIST_ENTRY * NameSpaceRefList
+ )
+{
+ EFI_STATUS Status;
+ AML_NAMESPACE_REF_NODE * NameSpaceRefNode;
+
+ AML_STREAM NodeNameFStream;
+ EAML_PARSE_INDEX NameIndex;
+ CONST AML_DATA_NODE * NameNode;
+
+ AML_STREAM RawAbsolutePathBStream;
+ UINT32 RawAbsolutePathBStreamSize;
+
+ CHAR8 * AbsolutePathBuffer;
+ UINT32 AbsolutePathBufferSize;
+
+ CONST AML_NODE_HEADER * ParentNode;
+
+ if (!AmlNodeHasAttribute (Node, AML_IN_NAMESPACE) ||
+ (NameSpaceRefList == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Allocate a buffer to get the raw AML absolute pathname of the
+ // namespace node.
+ AbsolutePathBufferSize = MAX_AML_NAMESTRING_SIZE;
+ AbsolutePathBuffer = AllocateZeroPool (AbsolutePathBufferSize);
+ if (AbsolutePathBuffer == NULL) {
+ ASSERT (0);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = AmlStreamInit (
+ &RawAbsolutePathBStream,
+ (UINT8*)AbsolutePathBuffer,
+ AbsolutePathBufferSize,
+ EAmlStreamDirectionBackward
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ Status = EFI_INVALID_PARAMETER;
+ goto exit_handler;
+ }
+
+ // Get the index where the name of the Node is stored in its
+ // fixed list of arguments.
+ Status = AmlNodeGetNameIndex (Node, &NameIndex);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ Status = EFI_INVALID_PARAMETER;
+ goto exit_handler;
+ }
+
+ // Get the Node name.
+ NameNode = (CONST AML_DATA_NODE*)AmlGetFixedArgument (
+ (AML_OBJECT_NODE*)Node,
+ NameIndex
+ );
+ if (!IS_AML_DATA_NODE (NameNode) ||
+ (NameNode->DataType != EAmlNodeDataTypeNameString)) {
+ ASSERT (0);
+ Status = EFI_INVALID_PARAMETER;
+ goto exit_handler;
+ }
+
+ // Initialize a stream on the node name of the namespace node.
+ // This is an AML NameString.
+ Status = AmlStreamInit (
+ &NodeNameFStream,
+ NameNode->Buffer,
+ NameNode->Size,
+ EAmlStreamDirectionForward
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ Status = EFI_INVALID_PARAMETER;
+ goto exit_handler;
+ }
+
+ ParentNode = AmlGetParent ((AML_NODE_HEADER*)Node);
+ if (ParentNode == NULL) {
+ ASSERT (0);
+ Status = EFI_INVALID_PARAMETER;
+ goto exit_handler;
+ }
+
+ // Build the raw AML absolute path of the namespace node.
+ Status = AmlBuildRawMethodAbsolutePath (
+ ParentNode,
+ &NodeNameFStream,
+ &RawAbsolutePathBStream
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler;
+ }
+
+ RawAbsolutePathBStreamSize = AmlStreamGetIndex (&RawAbsolutePathBStream);
+ // This is the root path: this cannot be a method invocation.
+ if (RawAbsolutePathBStreamSize == 0) {
+ Status = EFI_SUCCESS;
+ goto exit_handler;
+ }
+
+ // Create a NameSpace reference node.
+ Status = AmlCreateMethodRefNode (
+ Node,
+ (CONST CHAR8*)AmlStreamGetCurrPos (&RawAbsolutePathBStream),
+ RawAbsolutePathBStreamSize,
+ &NameSpaceRefNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto exit_handler;
+ }
+
+ // Add the created NameSpaceRefNode to the list.
+ InsertTailList (NameSpaceRefList, &NameSpaceRefNode->Link);
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "AmlMethodParser: Adding namespace reference with name:\n"
+ ));
+ AmlDbgPrintChars (
+ DEBUG_VERBOSE,
+ (CONST CHAR8*)AmlStreamGetCurrPos (&RawAbsolutePathBStream),
+ AmlStreamGetIndex (&RawAbsolutePathBStream)
+ );
+ DEBUG ((DEBUG_VERBOSE, "\n"));
+
+exit_handler:
+ // Free allocated memory.
+ FreePool (AbsolutePathBuffer);
+
+ return Status;
+}
+
+/** Create a method invocation node.
+
+ The AML grammar does not attribute an OpCode/SubOpCode couple for
+ method invocations. This library is representing method invocations
+ as if they had one.
+
+ The AML encoding for method invocations in the ACPI specification 6.3 is:
+ MethodInvocation := NameString TermArgList
+ In this library, it is:
+ MethodInvocation := MethodInvocationOp NameString ArgumentCount TermArgList
+ ArgumentCount := ByteData
+
+ When computing the size of a tree or serializing it, the additional data is
+ not taken into account (i.e. the MethodInvocationOp and the ArgumentCount).
+
+ Method invocation nodes have the AML_METHOD_INVOVATION attribute.
+
+ @param [in] NameSpaceRefNode NameSpaceRef node pointing to the
+ the definition of the invoked
+ method.
+ @param [in] MethodInvocationName Data node containing the method
+ invocation name.
+ @param [out] MethodInvocationNodePtr Created method invocation node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCreateMethodInvocationNode (
+ IN CONST AML_NAMESPACE_REF_NODE * NameSpaceRefNode,
+ IN AML_DATA_NODE * MethodInvocationName,
+ OUT AML_OBJECT_NODE ** MethodInvocationNodePtr
+ )
+{
+ EFI_STATUS Status;
+
+ UINT8 ArgCount;
+ AML_DATA_NODE * ArgCountNode;
+ AML_NODE_HEADER ** FixedArgs;
+ AML_OBJECT_NODE * MethodDefinitionNode;
+ AML_OBJECT_NODE * MethodInvocationNode;
+
+ if ((NameSpaceRefNode == NULL) ||
+ !AmlIsMethodDefinitionNode (NameSpaceRefNode->NodeRef) ||
+ !IS_AML_DATA_NODE (MethodInvocationName) ||
+ (MethodInvocationName->DataType != EAmlNodeDataTypeNameString) ||
+ (MethodInvocationNodePtr == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Get the number of arguments of the method.
+ MethodDefinitionNode = (AML_OBJECT_NODE*)NameSpaceRefNode->NodeRef;
+ FixedArgs = MethodDefinitionNode->FixedArgs;
+ // The method definition is an actual method definition.
+ if (AmlNodeCompareOpCode (MethodDefinitionNode, AML_METHOD_OP, 0)) {
+ // Cf ACPI 6.3 specification:
+ // DefMethod := MethodOp PkgLength NameString MethodFlags TermList
+ // MethodOp := 0x14
+ // MethodFlags := ByteData bit 0-2: ArgCount (0-7)
+ // bit 3: SerializeFlag
+ // 0 NotSerialized
+ // 1 Serialized
+ // bit 4-7: SyncLevel (0x00-0x0f)
+
+ // Read the MethodFlags to decode the ArgCount.
+ ArgCountNode = (AML_DATA_NODE*)FixedArgs[EAmlParseIndexTerm1];
+ ArgCount = *((UINT8*)ArgCountNode->Buffer) & 0x7;
+ } else if (AmlNodeCompareOpCode (MethodDefinitionNode, AML_EXTERNAL_OP, 0)) {
+ // The method definition is an external statement.
+ // Cf ACPI 6.3 specification:
+ // DefExternal := ExternalOp NameString ObjectType ArgumentCount
+ // ExternalOp := 0x15
+ // ObjectType := ByteData
+ // ArgumentCount := ByteData (0 – 7)
+
+ // Read the ArgumentCount.
+ ArgCountNode = (AML_DATA_NODE*)FixedArgs[EAmlParseIndexTerm2];
+ ArgCount = *((UINT8*)ArgCountNode->Buffer);
+ } else {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Create the object node for the method invocation.
+ // MethodInvocation := MethodInvocationOp NameString ArgumentCount
+ // MethodInvocationOp := Pseudo Opcode for Method Invocation
+ // NameString := Method Name
+ // ArgumentCount := ByteData (0 – 7)
+ Status = AmlCreateObjectNode (
+ AmlGetByteEncodingByOpCode (AML_METHOD_INVOC_OP, 0),
+ 0,
+ &MethodInvocationNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // The first fixed argument is the method name.
+ Status = AmlSetFixedArgument (
+ MethodInvocationNode,
+ EAmlParseIndexTerm0,
+ (AML_NODE_HEADER*)MethodInvocationName
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ // Create a data node holding the number of arguments
+ // of the method invocation.
+ ArgCountNode = NULL;
+ Status = AmlCreateDataNode (
+ EAmlNodeDataTypeUInt,
+ &ArgCount,
+ sizeof (UINT8),
+ &ArgCountNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ // The second fixed argument is the number of arguments.
+ Status = AmlSetFixedArgument (
+ MethodInvocationNode,
+ EAmlParseIndexTerm1,
+ (AML_NODE_HEADER*)ArgCountNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ *MethodInvocationNodePtr = MethodInvocationNode;
+ return Status;
+
+error_handler:
+ // Delete the sub-tree: the method invocation name is already attached.
+ AmlDeleteTree ((AML_NODE_HEADER*)MethodInvocationNode);
+ if (ArgCountNode != NULL) {
+ AmlDeleteNode ((AML_NODE_HEADER*)ArgCountNode);
+ }
+
+ return Status;
+}
+
+/** Get the number of arguments of a method invocation node.
+
+ This function also allow to identify whether a node is a method invocation
+ node. If the input node is not a method invocation node, just return.
+
+ @param [in] MethodInvocationNode Method invocation node.
+ @param [out] IsMethodInvocation Boolean stating whether the input
+ node is a method invocation.
+ @param [out] ArgCount Number of arguments of the method
+ invocation.
+ Set to 0 if MethodInvocationNode
+ is not a method invocation.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_BUFFER_TOO_SMALL No space left in the buffer.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+*/
+EFI_STATUS
+EFIAPI
+AmlGetMethodInvocationArgCount (
+ IN CONST AML_OBJECT_NODE * MethodInvocationNode,
+ OUT BOOLEAN * IsMethodInvocation,
+ OUT UINT8 * ArgCount
+ )
+{
+ AML_DATA_NODE * NumArgsNode;
+
+ if (!IS_AML_NODE_VALID (MethodInvocationNode) ||
+ (IsMethodInvocation == NULL) ||
+ (ArgCount == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Check whether MethodInvocationNode is a method invocation.
+ if (!AmlNodeCompareOpCode (MethodInvocationNode, AML_METHOD_INVOC_OP, 0)) {
+ *IsMethodInvocation = FALSE;
+ *ArgCount = 0;
+ return EFI_SUCCESS;
+ }
+
+ // MethodInvocation := MethodInvocationOp NameString ArgumentCount
+ // MethodInvocationOp := Pseudo Opcode for Method Invocation
+ // NameString := Method Name
+ // ArgumentCount := ByteData (0 - 7)
+ NumArgsNode = (AML_DATA_NODE*)AmlGetFixedArgument (
+ (AML_OBJECT_NODE*)MethodInvocationNode,
+ EAmlParseIndexTerm1
+ );
+ if (!IS_AML_NODE_VALID (NumArgsNode) ||
+ (NumArgsNode->Buffer == NULL) ||
+ (NumArgsNode->DataType != EAmlNodeDataTypeUInt) ||
+ (NumArgsNode->Size != 1)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+ *ArgCount = *NumArgsNode->Buffer;
+
+ *IsMethodInvocation = TRUE;
+ return EFI_SUCCESS;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h
new file mode 100644
index 0000000000..b082f2debd
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h
@@ -0,0 +1,188 @@
+/** @file
+ AML Method Parser.
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_METHOD_PARSER_H_
+#define AML_METHOD_PARSER_H_
+
+#include <AmlNodeDefines.h>
+#include <Stream/AmlStream.h>
+
+/** AML namespace reference node.
+
+ Namespace reference nodes allow to associate an AML absolute pathname
+ to the tree node defining this object in the namespace.
+
+ Namespace reference nodes are stored in a separate list. They are not part of
+ the tree.
+*/
+typedef struct AmlNameSpaceRefNode {
+ /// Double linked list.
+ /// This must be the first field in this structure.
+ LIST_ENTRY Link;
+
+ /// Node part of the AML namespace. It must have the AML_IN_NAMESPACE
+ /// attribute.
+ CONST AML_OBJECT_NODE * NodeRef;
+
+ /// Raw AML absolute pathname of the NodeRef.
+ /// This is a raw AML NameString (cf AmlNameSpace.c: A concatenated list
+ /// of 4 chars long names. The dual/multi NameString prefix have been
+ /// stripped.).
+ CONST CHAR8 * RawAbsolutePath;
+
+ /// Size of the raw AML absolute pathname buffer.
+ UINT32 RawAbsolutePathSize;
+} AML_NAMESPACE_REF_NODE;
+
+/** Delete a list of namespace reference nodes.
+
+ @param [in] NameSpaceRefList List of namespace reference nodes.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlDeleteNameSpaceRefList (
+ IN LIST_ENTRY * NameSpaceRefList
+ );
+
+
+#if !defined (MDEPKG_NDEBUG)
+/** Print the list of raw absolute paths of the NameSpace reference list.
+
+ @param [in] NameSpaceRefList List of NameSpace reference nodes.
+**/
+VOID
+EFIAPI
+AmlDbgPrintNameSpaceRefList (
+ IN CONST LIST_ENTRY * NameSpaceRefList
+ );
+
+#endif // MDEPKG_NDEBUG
+
+/** Check whether a pathname is a method invocation.
+
+ If there is a matching method definition, returns the corresponding
+ NameSpaceRef node.
+
+ To do so, the NameSpaceRefList is keeping track of every namespace node
+ and its raw AML absolute path.
+ To check whether a pathname is a method invocation, a corresponding raw
+ absolute pathname is built. This raw absolute pathname is then compared
+ to the list of available pathnames. If a pathname defining a method
+ matches the scope of the input pathname, return.
+
+ @param [in] ParentNode Parent node. Node to which the node to be
+ created will be attached.
+ @param [in] FStream Forward stream pointing to the NameString
+ to find.
+ @param [in] NameSpaceRefList List of NameSpaceRef nodes.
+ @param [out] OutNameSpaceRefNode If the NameString pointed by FStream is
+ a method invocation, OutNameSpaceRefNode
+ contains the NameSpaceRef corresponding
+ to the method definition.
+ NULL otherwise.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlIsMethodInvocation (
+ IN CONST AML_NODE_HEADER * ParentNode,
+ IN CONST AML_STREAM * FStream,
+ IN CONST LIST_ENTRY * NameSpaceRefList,
+ OUT AML_NAMESPACE_REF_NODE ** OutNameSpaceRefNode
+ );
+
+/** Create a namespace reference node and add it to the NameSpaceRefList.
+
+ When a namespace node is encountered, the namespace it defines must be
+ associated to the node. This allow to keep track of the nature of each
+ name present in the AML namespace.
+
+ In the end, this allows to recognize method invocations and parse the right
+ number of arguments after the method name.
+
+ @param [in] Node Namespace node.
+ @param [in, out] NameSpaceRefList List of namespace reference nodes.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlAddNameSpaceReference (
+ IN CONST AML_OBJECT_NODE * Node,
+ IN OUT LIST_ENTRY * NameSpaceRefList
+ );
+
+/** Create a method invocation node.
+
+ The AML grammar does not attribute an OpCode/SubOpCode couple for
+ method invocations. This library is representing method invocations
+ as if they had one.
+
+ The AML encoding for method invocations in the ACPI specification 6.3 is:
+ MethodInvocation := NameString TermArgList
+ In this library, it is:
+ MethodInvocation := MethodInvocationOp NameString ArgumentCount TermArgList
+ ArgumentCount := ByteData
+
+ When computing the size of a tree or serializing it, the additional data is
+ not taken into account (i.e. the MethodInvocationOp and the ArgumentCount).
+
+ Method invocation nodes have the AML_METHOD_INVOVATION attribute.
+
+ @param [in] NameSpaceRefNode NameSpaceRef node pointing to the
+ the definition of the invoked
+ method.
+ @param [in] MethodInvocationName Data node containing the method
+ invocation name.
+ @param [out] MethodInvocationNodePtr Created method invocation node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCreateMethodInvocationNode (
+ IN CONST AML_NAMESPACE_REF_NODE * NameSpaceRefNode,
+ IN AML_DATA_NODE * MethodInvocationName,
+ OUT AML_OBJECT_NODE ** MethodInvocationNodePtr
+ );
+
+/** Get the number of arguments of a method invocation node.
+
+ This function also allow to identify whether a node is a method invocation
+ node. If the input node is not a method invocation node, just return.
+
+ @param [in] MethodInvocationNode Method invocation node.
+ @param [out] IsMethodInvocation Boolean stating whether the input
+ node is a method invocation.
+ @param [out] ArgCount Number of arguments of the method
+ invocation.
+ Set to 0 if MethodInvocationNode
+ is not a method invocation.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_BUFFER_TOO_SMALL No space left in the buffer.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+*/
+EFI_STATUS
+EFIAPI
+AmlGetMethodInvocationArgCount (
+ IN CONST AML_OBJECT_NODE * MethodInvocationNode,
+ OUT BOOLEAN * IsMethodInvocation,
+ OUT UINT8 * ArgCount
+ );
+
+#endif // AML_METHOD_PARSER_H_