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