| /** @file | |
| AML NameSpace. | |
| Copyright (c) 2019 - 2021, Arm Limited. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| /* Lexicon: | |
| NameSeg: | |
| - An ASL NameSeg is a name made of at most 4 chars. | |
| Cf. ACPI 6.3 specification, s19.2.2 'Name and Pathname Terms'. | |
| - An AML NameSeg is a name made of 4 chars. | |
| Cf. ACPI 6.3 specification, s20.2.2 'Name Objects Encoding'. | |
| NameString: | |
| A NameString is analogous to a pathname. It is made of 0 to 255 NameSegs. | |
| A NameString can be prefixed by a root char ('\') or 0 to 255 carets ('^'). | |
| A NameString can be ASL or AML encoded. | |
| AML NameStrings can have a NameString prefix (dual or multi-name prefix) | |
| between the root/carets and the list of NameSegs. If the prefix is the | |
| multi-name prefix, then the number of NameSegs is encoded on one single byte. | |
| Cf. ACPI 6.3 specification, s19.2.2 'Name and Pathname Terms'. | |
| Cf. ACPI 6.3 specification, s20.2.2 'Name Objects Encoding'. | |
| Namespace level: | |
| One level in the AML Namespace level corresponds to one NameSeg. In ASL, | |
| objects names are NameStrings. This means a device can have a name which | |
| spans multiple levels. | |
| E.g.: The ASL code: Device (CLU0.CPU0) corresponds to 2 levels. | |
| Namespace node: | |
| A namespace node is an object node which has an associated name, and which | |
| changes the current scope. | |
| E.g.: | |
| 1. The "Device ()" ASL statement adds a name to the AML namespace and | |
| changes the current scope to the device scope, this is a namespace node. | |
| 2. The "Scope ()" ASL statement changes the current scope, this is a | |
| namespace node. | |
| 3. A method invocation has a name, but does not add nor change the current | |
| AML scope. This is not a namespace node. | |
| - Object nodes with the AML_IN_NAMESPACE attribute are namespace nodes. | |
| Buffers (), Packages (), etc. are not part of the namespace. It is however | |
| possible to associate them with a name with the Name () ASL statement. | |
| - The root node is considered as being part of the namespace. | |
| - Some resource data elements can have a name when defining them in | |
| an ASL statement. However, this name is stripped by the ASL compiler. | |
| Thus, they don't have a name in the AML bytestream, and are therefore | |
| not part of the AML namespace. | |
| - Field list elements are part of the namespace. | |
| Fields created by an CreateXXXField () ASL statement are part of the | |
| namespace. The name of these node can be found in the third or fourth | |
| fixed argument. The exact index of the name can be found in the NameIndex | |
| field of the AML_BYTE_ENCODING array. | |
| Field are at the same level as their ASL statement in the namespace. | |
| E.g: | |
| Scope (\) { | |
| OperationRegion (REG0, SystemIO, 0x100, 0x100) | |
| Field (REG0, ByteAcc, NoLock, Preserve) { | |
| FIE0, 1, | |
| FIE1, 5 | |
| } | |
| Name (BUF0, Buffer (100) {}) | |
| CreateField (BUF0, 5, 2, MEM0) | |
| } | |
| produces this namespace: | |
| \ (Root) | |
| \-REG0 | |
| \-FIE0 | |
| \-FIE1 | |
| \-BUF0 | |
| \-MEM0 | |
| Raw AML pathname or Raw AML NameString: | |
| In order to easily manipulate AML NameStrings, the non-NameSegs chars are | |
| removed in raw pathnames/NameStrings. Non-NameSegs chars are the | |
| root char ('\'), carets ('^') and NameString prefixes (Dual/Multi name char). | |
| E.g. The following terminology is defined in this AML Library. | |
| ASL absolute path: "[RootChar]AAAA.BBBB.CCCC\0" | |
| AML absolute path: "[RootChar][MultiNamePrefix][3(NameSegs)]AAAABBBBCCCC" | |
| Raw absolute path: "AAAABBBBCCCC" | |
| Multi-name: | |
| A NameString with at least 2 NameSegs. A node can have a name which spans | |
| multiple namespace levels. | |
| */ | |
| #include <NameSpace/AmlNameSpace.h> | |
| #include <AmlCoreInterface.h> | |
| #include <AmlDbgPrint/AmlDbgPrint.h> | |
| #include <String/AmlString.h> | |
| #include <Tree/AmlNode.h> | |
| #include <Tree/AmlTree.h> | |
| #include <Tree/AmlTreeTraversal.h> | |
| /** Context of the path search callback function. | |
| The function finding a node from a path and a reference node enumerates | |
| the namespace nodes in the tree and compares their absolute path with the | |
| searched path. The enumeration function uses a callback function that can | |
| receive a context. | |
| This structure is used to store the context information required in the | |
| callback function. | |
| */ | |
| typedef struct AmlPathSearchContext { | |
| /// Backward stream holding the raw AML absolute searched path. | |
| AML_STREAM *SearchPathBStream; | |
| /// An empty backward stream holding a pre-allocated buffer. This prevents | |
| /// from having to do multiple allocations during the search. | |
| /// This stream is used to query the raw AML absolute path of the node | |
| /// currently being probed. | |
| AML_STREAM *CurrNodePathBStream; | |
| /// If the node being visited is the node being searched, | |
| /// i.e. its path and the searched path match, | |
| /// save its reference in this pointer. | |
| AML_NODE_HEADER *OutNode; | |
| } AML_PATH_SEARCH_CONTEXT; | |
| /** Return the first AML namespace node up in the parent hierarchy. | |
| Return the root node if no namespace node is found is the hierarchy. | |
| @param [in] Node Node to look at the parents from. | |
| If Node is the root node, OutNode is NULL. | |
| @param [out] OutNode If a namespace node is found, pointer to the | |
| first namespace node of Node's parents. | |
| Stop at the root node otherwise. | |
| @retval EFI_SUCCESS The function completed successfully. | |
| @retval EFI_INVALID_PARAMETER Invalid parameter. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AmlGetFirstAncestorNameSpaceNode ( | |
| IN CONST AML_NODE_HEADER *Node, | |
| OUT AML_NODE_HEADER **OutNode | |
| ) | |
| { | |
| if (!IS_AML_NODE_VALID (Node) || | |
| (OutNode == NULL)) | |
| { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // If Node is the root node, return NULL. | |
| if (IS_AML_ROOT_NODE (Node)) { | |
| *OutNode = NULL; | |
| return EFI_SUCCESS; | |
| } else { | |
| // Else, get the parent node. | |
| Node = AmlGetParent ((AML_NODE_HEADER *)Node); | |
| if (!IS_AML_NODE_VALID (Node)) { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| } | |
| // Continue getting the parent node while no namespace node is encountered. | |
| while (TRUE) { | |
| if (IS_AML_ROOT_NODE (Node)) { | |
| break; | |
| } else if (AmlNodeHasAttribute ( | |
| (CONST AML_OBJECT_NODE *)Node, | |
| AML_IN_NAMESPACE | |
| )) | |
| { | |
| break; | |
| } else { | |
| Node = AmlGetParent ((AML_NODE_HEADER *)Node); | |
| if (!IS_AML_NODE_VALID (Node)) { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| } | |
| } // while | |
| *OutNode = (AML_NODE_HEADER *)Node; | |
| return EFI_SUCCESS; | |
| } | |
| /** Climb up the AML namespace hierarchy. | |
| This function get the ancestor namespace node in the AML namespace. | |
| If Levels is not zero, skip Levels namespace nodes in the AML namespace. | |
| If Levels is zero, return the first ancestor namespace node. | |
| I.e. if Levels = n, this function returns the (n + 1) ancestor. | |
| @param [in] Node Pointer to an object node. | |
| @param [in, out] Levels Pointer holding a number of AML namespace levels: | |
| - At entry, the number of levels to go up in | |
| the AML namespace; | |
| - At exit, the number of levels that still need | |
| to be climbed in case of a multi-named node. | |
| Indeed, if a node with a multi-name is found, | |
| and Levels is less than the number of NameSegs | |
| in this name, then the function returns with | |
| the number of levels that still need to be | |
| climbed. | |
| E.g.: If the first ancestor node's name is | |
| "AAAA.BBBB.CCCC" and | |
| Levels = 2 -> i.e go up 3 levels | |
| \ | |
| ... | |
| \-"AAAA.BBBB.CCCC" <----- OutNode | |
| \-"DDDD" <----- Node (Input) | |
| The function should ideally return a node | |
| with the name "AAAA". However, it is not | |
| possible to split the node name | |
| "AAAA.BBBB.CCCC" to "AAAA". | |
| Thus, OutNode is set to the input node, | |
| and Levels = 2. | |
| In most cases the number of levels to climb | |
| correspond to non multi-name node, and therefore | |
| Levels = 0 at exit. | |
| @param [out] HasRoot The returned node in OutNode has an AML absolute | |
| name, starting with a root char ('\'), or if OutNode | |
| is the root node. | |
| @param [out] OutNode The Levels+1 namespace ancestor of the input node in | |
| the AML namespace. Must be the root node or a | |
| namespace node. | |
| @retval EFI_SUCCESS The function completed successfully. | |
| @retval EFI_INVALID_PARAMETER Invalid parameter. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| AmlGetAncestorNameSpaceNode ( | |
| IN CONST AML_OBJECT_NODE *Node, | |
| IN OUT UINT32 *Levels, | |
| OUT UINT32 *HasRoot, | |
| OUT CONST AML_NODE_HEADER **OutNode | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| CONST AML_NODE_HEADER *NameSpaceNode; | |
| CHAR8 *NodeName; | |
| UINT32 ParentCnt; | |
| UINT32 Root; | |
| UINT32 ParentPrefix; | |
| UINT32 SegCount; | |
| if (!IS_AML_OBJECT_NODE (Node) || | |
| (Levels == NULL) || | |
| (HasRoot == NULL) || | |
| (OutNode == NULL)) | |
| { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| ParentCnt = *Levels; | |
| *HasRoot = 0; | |
| // ParentCnt namespace levels need to be climbed. | |
| do { | |
| // Get the next namespace node in the hierarchy. | |
| Status = AmlGetFirstAncestorNameSpaceNode ( | |
| (CONST AML_NODE_HEADER *)Node, | |
| (AML_NODE_HEADER **)&NameSpaceNode | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| Node = (CONST AML_OBJECT_NODE *)NameSpaceNode; | |
| if (IS_AML_ROOT_NODE (Node)) { | |
| // Node is the root node. It is not possible to go beyond. | |
| if (ParentCnt != 0) { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| *HasRoot = 1; | |
| break; | |
| } | |
| NodeName = AmlNodeGetName ((CONST AML_OBJECT_NODE *)Node); | |
| if (NodeName == NULL) { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // Analyze the node name. | |
| Status = AmlParseNameStringInfo ( | |
| NodeName, | |
| &Root, | |
| &ParentPrefix, | |
| &SegCount | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| if (Root != 0) { | |
| // NodeName is an absolute pathname. | |
| *HasRoot = Root; | |
| // If the node has Root then it cannot have ParentPrefixes (Carets). | |
| if (ParentPrefix != 0) { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (SegCount == ParentCnt) { | |
| // There are exactly enough AML namespace levels to consume. | |
| // This means the root node was the searched node. | |
| Node = (CONST AML_OBJECT_NODE *)AmlGetRootNode ( | |
| (CONST AML_NODE_HEADER *)Node | |
| ); | |
| if (!IS_AML_ROOT_NODE (Node)) { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| ParentCnt = 0; | |
| break; | |
| } else if (ParentCnt < SegCount) { | |
| // There are too many AML namespace levels in this name. | |
| // ParentCnt has the right value, just return. | |
| break; | |
| } else { | |
| // ParentCnt > SegCount | |
| // Return error as there must be at least ParentCnt AML namespace | |
| // levels left in the absolute path. | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| } else { | |
| // Root is 0. | |
| if (ParentCnt < SegCount) { | |
| // NodeName is a relative path. | |
| // NodeName has enough levels to consume all the ParentCnt. | |
| // Exit. | |
| break; | |
| } else if (SegCount == ParentCnt) { | |
| // There are exactly enough AML namespace levels to consume. | |
| if (ParentPrefix == 0) { | |
| // The node name doesn't have any carets. Get the next namespace | |
| // node and return. | |
| Status = AmlGetFirstAncestorNameSpaceNode ( | |
| (CONST AML_NODE_HEADER *)Node, | |
| (AML_NODE_HEADER **)&NameSpaceNode | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| Node = (CONST AML_OBJECT_NODE *)NameSpaceNode; | |
| ParentCnt = 0; | |
| break; | |
| } else { | |
| // The node name has carets. Need to continue climbing the | |
| // AML namespace. | |
| ParentCnt = ParentPrefix; | |
| } | |
| } else { | |
| // ParentCnt > SegCount | |
| // NodeName doesn't have enough levels to consume all the ParentCnt. | |
| // Update ParentCnt: Consume SegCount levels and add ParentPrefix | |
| // levels. Continue climbing the tree. | |
| ParentCnt = ParentCnt + ParentPrefix - SegCount; | |
| } | |
| } | |
| } while (ParentCnt != 0); | |
| *OutNode = (CONST AML_NODE_HEADER *)Node; | |
| *Levels = ParentCnt; | |
| return EFI_SUCCESS; | |
| } | |
| /** Build the raw absolute AML pathname to Node and write it to a stream. | |
| A raw AML pathname is an AML pathname where the root char ('\'), | |
| prefix chars ('^') and NameString prefix byte (e.g.: DualNamePrefix) | |
| have been removed. A raw AML pathname is a list of concatenated | |
| NameSegs. | |
| E.g.: | |
| ASL absolute path: "[RootChar]AAAA.BBBB.CCCC\0" | |
| AML absolute path: "[RootChar][MultiNamePrefix][3(NameSegs)]AAAABBBBCCCC" | |
| Raw absolute path: "AAAABBBBCCCC" | |
| @param [in] Node Node to build the raw absolute path to | |
| Must be a root node, or a namespace node. | |
| @param [in] InputParent Skip InputParent AML namespace levels before | |
| starting building the raw absolute pathname. | |
| E.g.: - Node's name being "^AAAA.BBBB.CCCC"; | |
| - InputParent = 2; | |
| "BBBB.CCCC" will be skipped (2 | |
| levels), and "^AAAA" will remain. The | |
| first caret is not related to InputParent. | |
| @param [out] RawAbsPathBStream Backward stream to write the raw | |
| pathname to. | |
| If Node is the root node, the Stream data | |
| Buffer will stay empty. | |
| 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. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AmlGetRawNameSpacePath ( | |
| IN CONST AML_NODE_HEADER *Node, | |
| IN UINT32 InputParent, | |
| OUT AML_STREAM *RawAbsPathBStream | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| AML_NODE_HEADER *ParentNode; | |
| CHAR8 *NodeName; | |
| UINT32 Root; | |
| UINT32 ParentPrefix; | |
| UINT32 SegCount; | |
| CONST CHAR8 *NameSeg; | |
| if ((!IS_AML_ROOT_NODE (Node) && | |
| !AmlNodeHasAttribute ( | |
| (CONST AML_OBJECT_NODE *)Node, | |
| AML_IN_NAMESPACE | |
| )) || | |
| !IS_STREAM (RawAbsPathBStream) || | |
| IS_END_OF_STREAM (RawAbsPathBStream) || | |
| !IS_STREAM_BACKWARD (RawAbsPathBStream) || | |
| (InputParent > MAX_UINT8)) | |
| { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| while (1) { | |
| if (IS_AML_ROOT_NODE (Node)) { | |
| break; | |
| } | |
| NodeName = AmlNodeGetName ((CONST AML_OBJECT_NODE *)Node); | |
| if (NodeName == NULL) { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Status = AmlParseNameStringInfo ( | |
| NodeName, | |
| &Root, | |
| &ParentPrefix, | |
| &SegCount | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| if (SegCount > InputParent) { | |
| // 1.1. If the Node's name has enough levels to consume all the | |
| // InputParent carets, write the levels that are left. | |
| NameSeg = AmlGetFirstNameSeg (NodeName, Root, ParentPrefix); | |
| Status = AmlStreamWrite ( | |
| RawAbsPathBStream, | |
| (CONST UINT8 *)NameSeg, | |
| (SegCount - InputParent) * AML_NAME_SEG_SIZE | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| InputParent = 0; | |
| } else { | |
| // (SegCount <= InputParent) | |
| // 1.2. Else save the InputParent in TotalParent to climb | |
| // them later. | |
| InputParent -= SegCount; | |
| } | |
| InputParent += ParentPrefix; | |
| if (Root != 0) { | |
| // 2. The Node's name is an absolute path. | |
| // Exit, the root has been reached. | |
| if (InputParent != 0) { | |
| ASSERT (0); | |
| return EFI_NOT_FOUND; | |
| } | |
| break; | |
| } | |
| Status = AmlGetAncestorNameSpaceNode ( | |
| (CONST AML_OBJECT_NODE *)Node, | |
| &InputParent, | |
| &Root, | |
| (CONST AML_NODE_HEADER **)&ParentNode | |
| ); | |
| if (EFI_ERROR (Status) || | |
| (!IS_AML_NODE_VALID (ParentNode))) | |
| { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| Node = ParentNode; | |
| if (IS_AML_ROOT_NODE (Node)) { | |
| // 3.1. If the root node has been found while climbing, | |
| // no need to write NameSegs. | |
| // Exit. | |
| break; | |
| } else if (Root != 0) { | |
| // 3.2. An absolute path has been found while climbing the tree. | |
| // If (InputParent != 0), the raw pathname is not the root. | |
| // Write the first [SegCount - InputParent] NameSegs of this | |
| // absolute path. | |
| // Then exit. | |
| if (InputParent != 0) { | |
| // Get the absolute pathname. | |
| NodeName = AmlNodeGetName ((CONST AML_OBJECT_NODE *)Node); | |
| if (NodeName == NULL) { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // Analyze the absolute pathname. | |
| Status = AmlParseNameStringInfo ( | |
| NodeName, | |
| &Root, | |
| &ParentPrefix, | |
| &SegCount | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| // Writing the n first NameSegs. | |
| // n = SegCount - InputParent | |
| NameSeg = AmlGetFirstNameSeg (NodeName, Root, ParentPrefix); | |
| Status = AmlStreamWrite ( | |
| RawAbsPathBStream, | |
| (CONST UINT8 *)NameSeg, | |
| (SegCount - InputParent) * AML_NAME_SEG_SIZE | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| break; | |
| } // (InputParent != 0) | |
| } | |
| } // while | |
| return EFI_SUCCESS; | |
| } | |
| /** Add the RootChar and prefix byte to the raw AML NameString in the | |
| input Stream to create a valid absolute path. | |
| The prefix byte can be AML_DUAL_NAME_PREFIX, AML_MULTI_NAME_PREFIX | |
| or nothing. | |
| @param [in, out] AmlPathBStream The Stream initially contains a raw | |
| NameString (i.e. a list of NameSegs). | |
| The Stream can be empty (e.g.: for the | |
| root path). | |
| 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 | |
| AmlAddPrefix ( | |
| IN OUT AML_STREAM *AmlPathBStream | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT32 NameSegCount; | |
| UINT32 NameSegSize; | |
| // At most 3 bytes are needed for: RootChar + MultiNamePrefix + SegCount. | |
| CHAR8 Prefix[3]; | |
| UINT32 PrefixSize; | |
| // The Stream contains concatenated NameSegs. | |
| if (!IS_STREAM (AmlPathBStream) || | |
| IS_END_OF_STREAM (AmlPathBStream) || | |
| !IS_STREAM_BACKWARD (AmlPathBStream)) | |
| { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // Its size should be a multiple of AML_NAME_SEG_SIZE. | |
| // AML_NAME_SEG_SIZE = 4. Check the 2 lowest bits. | |
| NameSegSize = AmlStreamGetIndex (AmlPathBStream); | |
| if ((NameSegSize & (AML_NAME_SEG_SIZE - 1)) != 0) { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // Each NameSeg is 4 bytes so divide the NameSegSize by 4. | |
| NameSegCount = NameSegSize >> 2; | |
| if (NameSegCount > MAX_UINT8) { | |
| // There can be at most 255 NameSegs. | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Prefix[0] = AML_ROOT_CHAR; | |
| switch (NameSegCount) { | |
| case 0: | |
| { | |
| // Root and parents only NameString (no NameSeg(s)) end with '\0'. | |
| Prefix[1] = AML_ZERO_OP; | |
| PrefixSize = 2; | |
| break; | |
| } | |
| case 1: | |
| { | |
| PrefixSize = 1; | |
| break; | |
| } | |
| case 2: | |
| { | |
| Prefix[1] = AML_DUAL_NAME_PREFIX; | |
| PrefixSize = 2; | |
| break; | |
| } | |
| default: | |
| { | |
| Prefix[1] = AML_MULTI_NAME_PREFIX; | |
| Prefix[2] = (UINT8)NameSegCount; | |
| PrefixSize = 3; | |
| break; | |
| } | |
| } | |
| // Add the RootChar + prefix (if needed) at the beginning of the pathname. | |
| Status = AmlStreamWrite (AmlPathBStream, (CONST UINT8 *)Prefix, PrefixSize); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| return Status; | |
| } | |
| /** Remove the prefix bytes of an AML NameString stored in a backward stream | |
| to get a raw NameString. | |
| The AML encoding for '\', '^', Dual name or multi-name prefix are | |
| stripped off. | |
| E.g: If the ASL path was "\AAAA.BBBB", the AML equivalent would be | |
| "{RootChar}{DualNamePrefix}AAAABBBB". So resultant raw NameString | |
| is "AAAABBBB". | |
| @param [in, out] AmlPathBStream Backward stream containing an AML | |
| NameString. | |
| The stream must not be at its end. | |
| @retval EFI_SUCCESS The function completed successfully. | |
| @retval EFI_INVALID_PARAMETER Invalid parameter. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| AmlRemovePrefix ( | |
| IN OUT AML_STREAM *AmlPathBStream | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT32 TotalSize; | |
| UINT32 RewindSize; | |
| UINT32 Root; | |
| UINT32 ParentPrefix; | |
| UINT32 SegCount; | |
| if (!IS_STREAM (AmlPathBStream) || | |
| IS_END_OF_STREAM (AmlPathBStream) || | |
| !IS_STREAM_BACKWARD (AmlPathBStream)) | |
| { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Status = AmlParseNameStringInfo ( | |
| (CHAR8 *)AmlStreamGetCurrPos (AmlPathBStream), | |
| &Root, | |
| &ParentPrefix, | |
| &SegCount | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| TotalSize = AmlComputeNameStringSize (Root, ParentPrefix, SegCount); | |
| if (TotalSize == 0) { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // Rewind the stream of all the bytes that are not SegCounts | |
| // to drop the prefix. | |
| RewindSize = TotalSize - (SegCount * AML_NAME_SEG_SIZE); | |
| if (RewindSize != 0) { | |
| Status = AmlStreamRewind (AmlPathBStream, RewindSize); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** Build the absolute ASL pathname to Node. | |
| BufferSize is always updated to the size of the pathname. | |
| If: | |
| - the content of BufferSize is >= to the size of the pathname AND; | |
| - Buffer is not NULL. | |
| then copy the pathname in the Buffer. A buffer of the size | |
| MAX_ASL_NAMESTRING_SIZE is big enough to receive any ASL pathname. | |
| @param [in] Node Node to build the absolute path to. | |
| Must be a root node, or a namespace node. | |
| @param [out] Buffer Buffer to write the path to. | |
| If NULL, only update *BufferSize. | |
| @param [in, out] BufferSize Pointer holding: | |
| - At entry, the size of the Buffer; | |
| - At exit, the size of the pathname. | |
| @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 Out of memory. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AmlGetAslPathName ( | |
| IN AML_NODE_HEADER *Node, | |
| OUT CHAR8 *Buffer, | |
| IN OUT UINT32 *BufferSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // Backward stream used to build the raw AML absolute path to the node. | |
| AML_STREAM RawAmlAbsPathBStream; | |
| CHAR8 *RawAmlAbsPathBuffer; | |
| UINT32 RawAmlAbsPathBufferSize; | |
| CHAR8 *AmlPathName; | |
| CHAR8 *AslPathName; | |
| UINT32 AslPathNameSize; | |
| UINT32 Root; | |
| UINT32 ParentPrefix; | |
| UINT32 SegCount; | |
| if ((!IS_AML_ROOT_NODE (Node) && | |
| !AmlNodeHasAttribute ( | |
| (CONST AML_OBJECT_NODE *)Node, | |
| AML_IN_NAMESPACE | |
| )) || | |
| (BufferSize == NULL)) | |
| { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| AslPathName = NULL; | |
| // Allocate a Stream to get the raw AML absolute pathname. | |
| RawAmlAbsPathBufferSize = MAX_AML_NAMESTRING_SIZE; | |
| RawAmlAbsPathBuffer = AllocateZeroPool (RawAmlAbsPathBufferSize); | |
| if (RawAmlAbsPathBuffer == NULL) { | |
| ASSERT (0); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Status = AmlStreamInit ( | |
| &RawAmlAbsPathBStream, | |
| (UINT8 *)RawAmlAbsPathBuffer, | |
| RawAmlAbsPathBufferSize, | |
| EAmlStreamDirectionBackward | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| // Get the raw pathname of the Node. The raw pathname being an | |
| // AML NameString without the RootChar and prefix byte. | |
| // It is a list of concatenated NameSegs. | |
| Status = AmlGetRawNameSpacePath (Node, 0, &RawAmlAbsPathBStream); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| // Add the RootChar and prefix byte. | |
| Status = AmlAddPrefix (&RawAmlAbsPathBStream); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| AmlPathName = (CHAR8 *)AmlStreamGetCurrPos (&RawAmlAbsPathBStream); | |
| // Analyze the NameString. | |
| Status = AmlParseNameStringInfo ( | |
| (CONST CHAR8 *)AmlPathName, | |
| &Root, | |
| &ParentPrefix, | |
| &SegCount | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| // Compute the size the ASL pathname will take. | |
| AslPathNameSize = AslComputeNameStringSize (Root, ParentPrefix, SegCount); | |
| if (AslPathNameSize == 0) { | |
| ASSERT (0); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto exit_handler; | |
| } | |
| // Input Buffer is large enough. Copy the pathname if the Buffer is valid. | |
| if ((Buffer != NULL) && (AslPathNameSize <= *BufferSize)) { | |
| Status = ConvertAmlNameToAslName (AmlPathName, &AslPathName); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto exit_handler; | |
| } | |
| CopyMem (Buffer, AslPathName, AslPathNameSize); | |
| } | |
| *BufferSize = AslPathNameSize; | |
| exit_handler: | |
| // Free allocated memory. | |
| FreePool (RawAmlAbsPathBuffer); | |
| if (AslPathName != NULL) { | |
| FreePool (AslPathName); | |
| } | |
| return Status; | |
| } | |
| #if !defined (MDEPKG_NDEBUG) | |
| /** Recursively print the pathnames in the AML namespace in Node's branch. | |
| @param [in] Node Pointer to a node. | |
| @param [in] Context An empty forward stream holding a pre-allocated | |
| buffer. This prevents from having to do multiple | |
| allocations during the enumeration. | |
| @param [in, out] Status At entry, contains the status returned by the | |
| last call to this exact function during the | |
| enumeration. | |
| As exit, contains the returned status of the | |
| call to this function. | |
| Optional, can be NULL. | |
| @retval TRUE if the enumeration can continue or has finished without | |
| interruption. | |
| @retval FALSE if the enumeration needs to stopped or has stopped. | |
| **/ | |
| STATIC | |
| BOOLEAN | |
| EFIAPI | |
| AmlDbgPrintNameSpaceCallback ( | |
| IN AML_NODE_HEADER *Node, | |
| IN VOID *Context, | |
| IN OUT EFI_STATUS *Status OPTIONAL | |
| ) | |
| { | |
| BOOLEAN ContinueEnum; | |
| EFI_STATUS Status1; | |
| AML_STREAM *CurrNodePathFStream; | |
| CHAR8 *CurrNodePathBuffer; | |
| UINT32 CurrNodePathBufferSize; | |
| ContinueEnum = TRUE; | |
| Status1 = EFI_SUCCESS; | |
| if (!IS_AML_NODE_VALID (Node) || | |
| (Context == NULL)) | |
| { | |
| ASSERT (0); | |
| Status1 = EFI_INVALID_PARAMETER; | |
| ContinueEnum = FALSE; | |
| goto exit_handler; | |
| } | |
| if (!IS_AML_ROOT_NODE (Node) && | |
| !AmlNodeHasAttribute ( | |
| (CONST AML_OBJECT_NODE *)Node, | |
| AML_IN_NAMESPACE | |
| )) | |
| { | |
| // Skip this node and continue enumeration. | |
| goto exit_handler; | |
| } | |
| if (IS_AML_ROOT_NODE (Node)) { | |
| DEBUG ((DEBUG_INFO, "\\\n")); | |
| } else if (AmlNodeHasAttribute ( | |
| (CONST AML_OBJECT_NODE *)Node, | |
| AML_IN_NAMESPACE | |
| )) | |
| { | |
| CurrNodePathFStream = (AML_STREAM *)Context; | |
| // Check the Context's content. | |
| if (!IS_STREAM (CurrNodePathFStream) || | |
| IS_END_OF_STREAM (CurrNodePathFStream) || | |
| !IS_STREAM_FORWARD (CurrNodePathFStream)) | |
| { | |
| ASSERT (0); | |
| Status1 = EFI_INVALID_PARAMETER; | |
| ContinueEnum = FALSE; | |
| goto exit_handler; | |
| } | |
| CurrNodePathBuffer = (CHAR8 *)AmlStreamGetBuffer (CurrNodePathFStream); | |
| CurrNodePathBufferSize = AmlStreamGetMaxBufferSize (CurrNodePathFStream); | |
| Status1 = AmlGetAslPathName ( | |
| (AML_NODE_HEADER *)Node, | |
| CurrNodePathBuffer, | |
| &CurrNodePathBufferSize | |
| ); | |
| if (EFI_ERROR (Status1)) { | |
| ASSERT (0); | |
| ContinueEnum = FALSE; | |
| goto exit_handler; | |
| } | |
| DEBUG ((DEBUG_INFO, "%a\n", CurrNodePathBuffer)); | |
| } else { | |
| ASSERT (0); | |
| Status1 = EFI_INVALID_PARAMETER; | |
| ContinueEnum = FALSE; | |
| } | |
| exit_handler: | |
| if (Status != NULL) { | |
| *Status = Status1; | |
| } | |
| return ContinueEnum; | |
| } | |
| /** Print the absolute pathnames in the AML namespace of | |
| all the nodes in the tree starting from the Root node. | |
| @param [in] RootNode Pointer to a root node. | |
| @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 Out of memory. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AmlDbgPrintNameSpace ( | |
| IN AML_ROOT_NODE *RootNode | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| AML_STREAM CurrNodePathFStream; | |
| CHAR8 *CurrNodePathBuffer; | |
| UINT32 CurrNodePathBufferSize; | |
| if (!IS_AML_ROOT_NODE (RootNode)) { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| DEBUG ((DEBUG_INFO, "AmlNameSpace: AML namespace:\n")); | |
| // Allocate memory to build the absolute ASL path to each node. | |
| CurrNodePathBufferSize = MAX_AML_NAMESTRING_SIZE; | |
| CurrNodePathBuffer = AllocateZeroPool (CurrNodePathBufferSize); | |
| if (CurrNodePathBuffer == NULL) { | |
| ASSERT (0); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| // An empty forward stream holding a pre-allocated buffer is used | |
| // to avoid multiple allocations during the enumeration. | |
| Status = AmlStreamInit ( | |
| &CurrNodePathFStream, | |
| (UINT8 *)CurrNodePathBuffer, | |
| CurrNodePathBufferSize, | |
| EAmlStreamDirectionForward | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| AmlEnumTree ( | |
| (AML_NODE_HEADER *)RootNode, | |
| AmlDbgPrintNameSpaceCallback, | |
| (VOID *)&CurrNodePathFStream, | |
| &Status | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| exit_handler: | |
| FreePool (CurrNodePathBuffer); | |
| return Status; | |
| } | |
| #endif // MDEPKG_NDEBUG | |
| /** Callback function to find the node corresponding to an absolute pathname. | |
| For each namespace node, build its raw AML absolute path. Then compare this | |
| path with the raw AML absolute path of the search node available in the | |
| Context. | |
| @param [in] Node Pointer to the node to whose pathname is being | |
| tested. | |
| @param [in, out] Context A pointer to AML_PATH_SEARCH_CONTEXT that has: | |
| - The searched path stored in a stream; | |
| - An empty stream to query the pathname of the | |
| probed node; | |
| - A node pointer to store the searched node | |
| if found. | |
| @param [in, out] Status At entry, contains the status returned by the | |
| last call to this exact function during the | |
| enumeration. | |
| As exit, contains the returned status of the | |
| call to this function. | |
| Optional, can be NULL. | |
| @retval TRUE if the enumeration can continue or has finished without | |
| interruption. | |
| @retval FALSE if the enumeration needs to stopped or has stopped. | |
| **/ | |
| STATIC | |
| BOOLEAN | |
| EFIAPI | |
| AmlEnumeratePathCallback ( | |
| IN AML_NODE_HEADER *Node, | |
| IN OUT VOID *Context, | |
| IN OUT EFI_STATUS *Status OPTIONAL | |
| ) | |
| { | |
| BOOLEAN ContinueEnum; | |
| EFI_STATUS Status1; | |
| AML_PATH_SEARCH_CONTEXT *PathSearchContext; | |
| AML_STREAM *SearchPathBStream; | |
| AML_STREAM *CurrNodePathBStream; | |
| UINT32 CurrNodePathSize; | |
| ContinueEnum = TRUE; | |
| Status1 = EFI_SUCCESS; | |
| if (!IS_AML_NODE_VALID (Node) || | |
| (Context == NULL)) | |
| { | |
| ASSERT (0); | |
| Status1 = EFI_INVALID_PARAMETER; | |
| ContinueEnum = FALSE; | |
| goto exit_handler; | |
| } | |
| if (!AmlNodeHasAttribute ( | |
| (CONST AML_OBJECT_NODE *)Node, | |
| AML_IN_NAMESPACE | |
| )) | |
| { | |
| goto exit_handler; | |
| } | |
| PathSearchContext = (AML_PATH_SEARCH_CONTEXT *)Context; | |
| SearchPathBStream = PathSearchContext->SearchPathBStream; | |
| CurrNodePathBStream = PathSearchContext->CurrNodePathBStream; | |
| // Check the Context's content. | |
| if (!IS_STREAM (SearchPathBStream) || | |
| IS_END_OF_STREAM (SearchPathBStream) || | |
| !IS_STREAM_BACKWARD (SearchPathBStream) || | |
| !IS_STREAM (CurrNodePathBStream) || | |
| IS_END_OF_STREAM (CurrNodePathBStream) || | |
| !IS_STREAM_BACKWARD (CurrNodePathBStream)) | |
| { | |
| ASSERT (0); | |
| Status1 = EFI_INVALID_PARAMETER; | |
| ContinueEnum = FALSE; | |
| goto exit_handler; | |
| } | |
| CurrNodePathSize = AmlStreamGetMaxBufferSize (CurrNodePathBStream); | |
| if (CurrNodePathSize == 0) { | |
| ASSERT (0); | |
| Status1 = EFI_INVALID_PARAMETER; | |
| ContinueEnum = FALSE; | |
| goto exit_handler; | |
| } | |
| // Get the raw AML absolute pathname of the current node. | |
| Status1 = AmlGetRawNameSpacePath (Node, 0, CurrNodePathBStream); | |
| if (EFI_ERROR (Status1)) { | |
| ASSERT (0); | |
| ContinueEnum = FALSE; | |
| goto exit_handler; | |
| } | |
| DEBUG (( | |
| DEBUG_VERBOSE, | |
| "AmlNameSpace: " | |
| "Comparing search path with current node path.\n" | |
| )); | |
| DEBUG ((DEBUG_VERBOSE, "Search path:")); | |
| AMLDBG_PRINT_CHARS ( | |
| DEBUG_VERBOSE, | |
| (CHAR8 *)AmlStreamGetCurrPos (SearchPathBStream), | |
| AmlStreamGetIndex (SearchPathBStream) | |
| ); | |
| DEBUG ((DEBUG_VERBOSE, "\nPath of the current node: ")); | |
| AMLDBG_PRINT_CHARS ( | |
| DEBUG_VERBOSE, | |
| (CHAR8 *)AmlStreamGetCurrPos (CurrNodePathBStream), | |
| AmlStreamGetIndex (CurrNodePathBStream) | |
| ); | |
| DEBUG ((DEBUG_VERBOSE, "\n")); | |
| // Compare the searched path and Node's path. | |
| if ((AmlStreamGetIndex (CurrNodePathBStream) == | |
| AmlStreamGetIndex (SearchPathBStream)) && | |
| (CompareMem ( | |
| AmlStreamGetCurrPos (CurrNodePathBStream), | |
| AmlStreamGetCurrPos (SearchPathBStream), | |
| AmlStreamGetIndex (SearchPathBStream) | |
| ) == 0)) | |
| { | |
| Status1 = EFI_SUCCESS; | |
| ContinueEnum = FALSE; | |
| PathSearchContext->OutNode = Node; | |
| } else { | |
| // If the paths don't match, reset the CurrNodePathStream's content. | |
| Status1 = AmlStreamReset (CurrNodePathBStream); | |
| if (EFI_ERROR (Status1)) { | |
| ASSERT (0); | |
| ContinueEnum = FALSE; | |
| } | |
| } | |
| exit_handler: | |
| if (Status != NULL) { | |
| *Status = Status1; | |
| } | |
| return ContinueEnum; | |
| } | |
| /** Build a raw AML absolute path from a reference node and a relative | |
| ASL path. | |
| The AslPath can be a relative path or an absolute path. | |
| Node must be a root node or a namespace node. | |
| A root node is expected to be at the top of the tree. | |
| @param [in] ReferenceNode Reference node. | |
| If a relative path is given, the | |
| search is done from this node. If | |
| an absolute path is given, the | |
| search is done from the root node. | |
| Must be a root node or an object | |
| node which is part of the | |
| namespace. | |
| @param [in] AslPath ASL path to the searched node in | |
| the namespace. An ASL path name is | |
| NULL terminated. Can be a relative | |
| or absolute path. | |
| E.g.: "\\_SB.CLU0.CPU0". | |
| @param [in, out] RawAmlAbsSearchPathBStream Backward stream to write the | |
| raw absolute AML path of the | |
| searched node. | |
| 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. | |
| @retval EFI_OUT_OF_RESOURCES Out of memory. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| AmlBuildAbsoluteAmlPath ( | |
| IN AML_NODE_HEADER *ReferenceNode, | |
| IN CONST CHAR8 *AslPath, | |
| IN OUT AML_STREAM *RawAmlAbsSearchPathBStream | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| CHAR8 *AmlPath; | |
| UINT32 AmlNameStringSize; | |
| UINT32 Root; | |
| UINT32 ParentPrefix; | |
| UINT32 SegCount; | |
| if ((!IS_AML_ROOT_NODE (ReferenceNode) && | |
| !AmlNodeHasAttribute ( | |
| (CONST AML_OBJECT_NODE *)ReferenceNode, | |
| AML_IN_NAMESPACE | |
| )) || | |
| (AslPath == NULL) || | |
| !IS_STREAM (RawAmlAbsSearchPathBStream) || | |
| IS_END_OF_STREAM (RawAmlAbsSearchPathBStream) || | |
| !IS_STREAM_BACKWARD (RawAmlAbsSearchPathBStream)) | |
| { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // 1. Validate, analyze and convert the AslPath to an AmlPath. | |
| Status = ConvertAslNameToAmlName (AslPath, &AmlPath); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| Status = AmlParseNameStringInfo (AmlPath, &Root, &ParentPrefix, &SegCount); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| // Not possible to go beyond the root. | |
| if (IS_AML_ROOT_NODE (ReferenceNode) && (ParentPrefix != 0)) { | |
| Status = EFI_INVALID_PARAMETER; | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| AmlNameStringSize = AmlComputeNameStringSize (Root, ParentPrefix, SegCount); | |
| if (AmlNameStringSize == 0) { | |
| Status = EFI_INVALID_PARAMETER; | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| // 2.1. Write the AML path to the stream. | |
| Status = AmlStreamWrite ( | |
| RawAmlAbsSearchPathBStream, | |
| (CONST UINT8 *)AmlPath, | |
| AmlNameStringSize | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| // 2.2. Then remove the AML prefix (root char, parent prefix, etc.) | |
| // to obtain a raw AML NameString. Raw AML NameString are easier | |
| // to manipulate. | |
| Status = AmlRemovePrefix (RawAmlAbsSearchPathBStream); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| // 3. If AslPath is a relative path and the reference Node is not | |
| // the root node, fill the Stream with the absolute path to the | |
| // reference node. | |
| if ((Root == 0) && !IS_AML_ROOT_NODE (ReferenceNode)) { | |
| Status = AmlGetRawNameSpacePath ( | |
| ReferenceNode, | |
| ParentPrefix, | |
| RawAmlAbsSearchPathBStream | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| } | |
| } | |
| exit_handler: | |
| // Free allocated memory. | |
| FreePool (AmlPath); | |
| return Status; | |
| } | |
| /** Find a node in the AML namespace, given an ASL path and a reference Node. | |
| - The AslPath can be an absolute path, or a relative path from the | |
| reference Node; | |
| - Node must be a root node or a namespace node; | |
| - A root node is expected to be at the top of the tree. | |
| E.g.: | |
| For the following AML namespace, with the ReferenceNode being the node with | |
| the name "AAAA": | |
| - the node with the name "BBBB" can be found by looking for the ASL | |
| path "BBBB"; | |
| - the root node can be found by looking for the ASL relative path "^", | |
| or the absolute path "\\". | |
| AML namespace: | |
| \ | |
| \-AAAA <- ReferenceNode | |
| \-BBBB | |
| @param [in] ReferenceNode Reference node. | |
| If a relative path is given, the | |
| search is done from this node. If | |
| an absolute path is given, the | |
| search is done from the root node. | |
| Must be a root node or an object | |
| node which is part of the | |
| namespace. | |
| @param [in] AslPath ASL path to the searched node in | |
| the namespace. An ASL path name is | |
| NULL terminated. Can be a relative | |
| or absolute path. | |
| E.g.: "\\_SB.CLU0.CPU0" or "^CPU0" | |
| @param [out] OutNode Pointer to the found node. | |
| Contains NULL if not found. | |
| @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 Out of memory. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AmlFindNode ( | |
| IN AML_NODE_HEADER *ReferenceNode, | |
| IN CONST CHAR8 *AslPath, | |
| OUT AML_NODE_HEADER **OutNode | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| AML_PATH_SEARCH_CONTEXT PathSearchContext; | |
| AML_ROOT_NODE *RootNode; | |
| // Backward stream used to build the raw AML absolute path to the searched | |
| // node. | |
| AML_STREAM RawAmlAbsSearchPathBStream; | |
| CHAR8 *RawAmlAbsSearchPathBuffer; | |
| UINT32 RawAmlAbsSearchPathBufferSize; | |
| // Backward stream used to store the raw AML absolute path of the node | |
| // currently enumerated in the tree. This path can then be compared to the | |
| // RawAmlAbsSearchPath. | |
| AML_STREAM RawAmlAbsCurrNodePathBStream; | |
| CHAR8 *RawAmlAbsCurrNodePathBuffer; | |
| UINT32 RawAmlAbsCurrNodePathBufferSize; | |
| if ((!IS_AML_ROOT_NODE (ReferenceNode) && | |
| !AmlNodeHasAttribute ( | |
| (CONST AML_OBJECT_NODE *)ReferenceNode, | |
| AML_IN_NAMESPACE | |
| )) || | |
| (AslPath == NULL) || | |
| (OutNode == NULL)) | |
| { | |
| ASSERT (0); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| *OutNode = NULL; | |
| RawAmlAbsCurrNodePathBuffer = NULL; | |
| // 1. Build a raw absolute AML path from the reference node and the ASL | |
| // path. For this: | |
| // 1.1. First initialize a backward stream. | |
| RawAmlAbsSearchPathBufferSize = MAX_AML_NAMESTRING_SIZE; | |
| RawAmlAbsSearchPathBuffer = AllocateZeroPool (RawAmlAbsSearchPathBufferSize); | |
| if (RawAmlAbsSearchPathBuffer == NULL) { | |
| ASSERT (0); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Status = AmlStreamInit ( | |
| &RawAmlAbsSearchPathBStream, | |
| (UINT8 *)RawAmlAbsSearchPathBuffer, | |
| RawAmlAbsSearchPathBufferSize, | |
| EAmlStreamDirectionBackward | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| // 1.2. Then build the raw AML absolute path. | |
| Status = AmlBuildAbsoluteAmlPath ( | |
| ReferenceNode, | |
| AslPath, | |
| &RawAmlAbsSearchPathBStream | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| // 2. Find the root node by climbing up the tree from the reference node. | |
| RootNode = AmlGetRootNode (ReferenceNode); | |
| if (RootNode == NULL) { | |
| ASSERT (0); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto exit_handler; | |
| } | |
| // 3. If the searched node is the root node, return. | |
| // For the Root Node there is no NameSegs so the length of | |
| // the stream will be zero. | |
| if (AmlStreamGetIndex (&RawAmlAbsSearchPathBStream) == 0) { | |
| *OutNode = (AML_NODE_HEADER *)RootNode; | |
| Status = EFI_SUCCESS; | |
| goto exit_handler; | |
| } | |
| // 4. Create a backward stream large enough to hold the current node path | |
| // during enumeration. This prevents from doing multiple allocation/free | |
| // operations. | |
| RawAmlAbsCurrNodePathBufferSize = MAX_ASL_NAMESTRING_SIZE; | |
| RawAmlAbsCurrNodePathBuffer = AllocateZeroPool ( | |
| RawAmlAbsCurrNodePathBufferSize | |
| ); | |
| if (RawAmlAbsCurrNodePathBuffer == NULL) { | |
| ASSERT (0); | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto exit_handler; | |
| } | |
| Status = AmlStreamInit ( | |
| &RawAmlAbsCurrNodePathBStream, | |
| (UINT8 *)RawAmlAbsCurrNodePathBuffer, | |
| RawAmlAbsCurrNodePathBufferSize, | |
| EAmlStreamDirectionBackward | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| // 5. Fill a path search context structure with: | |
| // - SearchPathStream: backward stream containing the raw absolute AML | |
| // path to the searched node; | |
| // - CurrNodePathStream: backward stream containing the raw absolute AML | |
| // of the node currently being enumerated; | |
| // - OutNode: node pointer to the store the potentially found node. | |
| PathSearchContext.SearchPathBStream = &RawAmlAbsSearchPathBStream; | |
| PathSearchContext.CurrNodePathBStream = &RawAmlAbsCurrNodePathBStream; | |
| PathSearchContext.OutNode = NULL; | |
| // 6. Iterate through the namespace nodes of the tree. | |
| // For each namespace node, build its raw AML absolute path. Then compare | |
| // it with the search path. | |
| AmlEnumTree ( | |
| (AML_NODE_HEADER *)RootNode, | |
| AmlEnumeratePathCallback, | |
| (VOID *)&PathSearchContext, | |
| &Status | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| *OutNode = PathSearchContext.OutNode; | |
| if (*OutNode == NULL) { | |
| Status = EFI_NOT_FOUND; | |
| } | |
| exit_handler: | |
| // Free allocated memory. | |
| FreePool (RawAmlAbsSearchPathBuffer); | |
| if (RawAmlAbsCurrNodePathBuffer != NULL) { | |
| FreePool (RawAmlAbsCurrNodePathBuffer); | |
| } | |
| return Status; | |
| } |