| /** @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; | |
| AMLDBG_PRINT_CHARS ( | |
| 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: ")); | |
| AMLDBG_PRINT_CHARS ( | |
| 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. | |
| AMLDBG_PRINT_CHARS ( | |
| 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: " | |
| )); | |
| AMLDBG_PRINT_CHARS ( | |
| 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" | |
| )); | |
| AMLDBG_PRINT_CHARS ( | |
| 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; | |
| } |