From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../Library/Common/AmlLib/AmlCoreInterface.h | 767 ++++++++++ .../Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c | 546 +++++++ .../Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h | 154 ++ .../Library/Common/AmlLib/AmlDefines.h | 188 +++ .../Library/Common/AmlLib/AmlEncoding/Aml.c | 805 +++++++++++ .../Library/Common/AmlLib/AmlEncoding/Aml.h | 330 +++++ .../Library/Common/AmlLib/AmlInclude.h | 18 + .../Library/Common/AmlLib/AmlLib.inf | 76 + .../Library/Common/AmlLib/AmlNodeDefines.h | 183 +++ .../Library/Common/AmlLib/Api/AmlApi.c | 382 +++++ .../Library/Common/AmlLib/Api/AmlApiHelper.c | 219 +++ .../Library/Common/AmlLib/Api/AmlApiHelper.h | 93 ++ .../Library/Common/AmlLib/Api/AmlResourceDataApi.c | 320 +++++ .../Library/Common/AmlLib/CodeGen/AmlCodeGen.c | 701 +++++++++ .../Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c | 256 ++++ .../Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h | 59 + .../Library/Common/AmlLib/NameSpace/AmlNameSpace.c | 1501 ++++++++++++++++++++ .../Library/Common/AmlLib/NameSpace/AmlNameSpace.h | 74 + .../Common/AmlLib/Parser/AmlFieldListParser.c | 375 +++++ .../Common/AmlLib/Parser/AmlFieldListParser.h | 77 + .../Library/Common/AmlLib/Parser/AmlMethodParser.c | 1458 +++++++++++++++++++ .../Library/Common/AmlLib/Parser/AmlMethodParser.h | 188 +++ .../Library/Common/AmlLib/Parser/AmlParser.c | 1448 +++++++++++++++++++ .../Library/Common/AmlLib/Parser/AmlParser.h | 72 + .../Common/AmlLib/Parser/AmlResourceDataParser.c | 328 +++++ .../Common/AmlLib/Parser/AmlResourceDataParser.h | 71 + .../Common/AmlLib/ResourceData/AmlResourceData.c | 103 ++ .../Common/AmlLib/ResourceData/AmlResourceData.h | 174 +++ .../Library/Common/AmlLib/Serialize/AmlSerialize.c | 324 +++++ .../Library/Common/AmlLib/Stream/AmlStream.c | 665 +++++++++ .../Library/Common/AmlLib/Stream/AmlStream.h | 451 ++++++ .../Library/Common/AmlLib/String/AmlString.c | 1022 +++++++++++++ .../Library/Common/AmlLib/String/AmlString.h | 401 ++++++ .../Library/Common/AmlLib/Tree/AmlClone.c | 205 +++ .../Library/Common/AmlLib/Tree/AmlNode.c | 673 +++++++++ .../Library/Common/AmlLib/Tree/AmlNode.h | 212 +++ .../Library/Common/AmlLib/Tree/AmlNodeInterface.c | 566 ++++++++ .../Library/Common/AmlLib/Tree/AmlTree.c | 1047 ++++++++++++++ .../Library/Common/AmlLib/Tree/AmlTree.h | 127 ++ .../Library/Common/AmlLib/Tree/AmlTreeEnumerator.c | 98 ++ .../Library/Common/AmlLib/Tree/AmlTreeIterator.c | 353 +++++ .../Library/Common/AmlLib/Tree/AmlTreeIterator.h | 220 +++ .../Library/Common/AmlLib/Tree/AmlTreeTraversal.c | 548 +++++++ .../Library/Common/AmlLib/Tree/AmlTreeTraversal.h | 138 ++ .../Library/Common/AmlLib/Utils/AmlUtility.c | 906 ++++++++++++ .../Library/Common/AmlLib/Utils/AmlUtility.h | 95 ++ 46 files changed, 19017 insertions(+) create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlCoreInterface.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlDefines.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlInclude.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Serialize/AmlSerialize.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c create mode 100644 roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h (limited to 'roms/edk2/DynamicTablesPkg/Library/Common/AmlLib') diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlCoreInterface.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlCoreInterface.h new file mode 100644 index 000000000..9905cfe55 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlCoreInterface.h @@ -0,0 +1,767 @@ +/** @file + AML Core Interface. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_CORE_INTERFACE_H_ +#define AML_CORE_INTERFACE_H_ + +/* This header file does not include internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. The node definitions + must be included by the caller file. The function prototypes must + only expose AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, etc. node + definitions. + This allows to keep the functions defined here both internal and + potentially external. If necessary, any function of this file can + be exposed externally. + The Api folder is internal to the AmlLib, but should only use these + functions. They provide a "safe" way to interact with the AmlLib. +*/ + +#include +#include +#include + +/** + @defgroup CoreApis Core APIs + @ingroup AMLLib + @{ + Core APIs are the main APIs of the library. They allow to: + - Create an AML tree; + - Delete an AML tree; + - Clone an AML tree/node; + - Serialize an AML tree (convert the tree to a DSDT/SSDT table). + @} +*/ + +/** Serialize a tree to create a DSDT/SSDT table. + + If: + - the content of BufferSize is >= to the size needed to serialize the + definition block; + - Buffer is not NULL; + first serialize the ACPI DSDT/SSDT header from the root node, + then serialize the AML blob from the rest of the tree. + + The content of BufferSize is always updated to the size needed to + serialize the definition block. + + @ingroup CoreApis + + @param [in] RootNode Pointer to a root node. + @param [in] Buffer Buffer to write the DSDT/SSDT table to. + If Buffer is NULL, the size needed to + serialize the DSDT/SSDT table is returned + in BufferSize. + @param [in, out] BufferSize Pointer holding the size of the Buffer. + Its content is always updated to the size + needed to serialize the DSDT/SSDT table. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. +**/ +EFI_STATUS +EFIAPI +AmlSerializeTree ( + IN AML_ROOT_NODE_HANDLE RootNode, + IN UINT8 * Buffer, OPTIONAL + IN OUT UINT32 * BufferSize + ); + +/** Clone a node. + + This function does not clone the children nodes. + The cloned node returned is not attached to any tree. + + @ingroup CoreApis + + @param [in] Node Pointer to a node. + @param [out] ClonedNode Pointer holding the cloned 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 +AmlCloneNode ( + IN AML_NODE_HANDLE Node, + OUT AML_NODE_HANDLE * ClonedNode + ); + +/** + @defgroup TreeModificationApis Tree modification APIs + @ingroup AMLLib + @{ + Tree modification APIs allow to add/remove/replace nodes that are in a + variable list of arguments. + + No interface is provided to add/remove/replace nodes that are in a fixed + list of arguments. Indeed, these nodes are the spine of the tree and a + mismanipulation would make the tree inconsistent. + + It is however possible to modify the content of fixed argument nodes via + @ref NodeInterfaceApis APIs. + @} +*/ + +/** Remove the Node from its parent's variable list of arguments. + + The function will fail if the Node is in its parent's fixed + argument list. + The Node is not deleted. The deletion is done separately + from the removal. + + @ingroup TreeModificationApis + + @param [in] Node Pointer to a Node. + Must be a data node or an object node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlRemoveNodeFromVarArgList ( + IN AML_NODE_HANDLE Node + ); + +/** Add the NewNode to the head of the variable list of arguments + of the ParentNode. + + @ingroup TreeModificationApis + + @param [in] ParentNode Pointer to the parent node. + Must be a root or an object node. + @param [in] NewNode Pointer to the node to add. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlVarListAddHead ( + IN AML_NODE_HANDLE ParentNode, + IN AML_NODE_HANDLE NewNode + ); + +/** Add the NewNode to the tail of the variable list of arguments + of the ParentNode. + + @ingroup TreeModificationApis + + @param [in] ParentNode Pointer to the parent node. + Must be a root or an object node. + @param [in] NewNode Pointer to the node to add. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlVarListAddTail ( + IN AML_NODE_HANDLE ParentNode, + IN AML_NODE_HANDLE NewNode + ); + +/** Add the NewNode before the Node in the list of variable + arguments of the Node's parent. + + @ingroup TreeModificationApis + + @param [in] Node Pointer to a node. + Must be a root or an object node. + @param [in] NewNode Pointer to the node to add. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlVarListAddBefore ( + IN AML_NODE_HANDLE Node, + IN AML_NODE_HANDLE NewNode + ); + +/** Add the NewNode after the Node in the variable list of arguments + of the Node's parent. + + @ingroup TreeModificationApis + + @param [in] Node Pointer to a node. + Must be a root or an object node. + @param [in] NewNode Pointer to the node to add. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlVarListAddAfter ( + IN AML_NODE_HANDLE Node, + IN AML_NODE_HANDLE NewNode + ); + +/** Append a Resource Data node to the BufferOpNode. + + The Resource Data node is added at the end of the variable + list of arguments of the BufferOpNode, but before the End Tag. + If no End Tag is found, the function returns an error. + + @param [in] BufferOpNode Buffer node containing resource data elements. + @param [in] NewRdNode The new Resource Data node to add. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlAppendRdNode ( + IN AML_OBJECT_NODE_HANDLE BufferOpNode, + IN AML_DATA_NODE_HANDLE NewRdNode + ); + +/** Replace the OldNode, which is in a variable list of arguments, + with the NewNode. + + Note: This function unlinks the OldNode from the tree. It is the callers + responsibility to delete the OldNode if needed. + + @ingroup TreeModificationApis + + @param [in] OldNode Pointer to the node to replace. + Must be a data node or an object node. + @param [in] NewNode The new node to insert. + Must be a data node or an object node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlReplaceVariableArgument ( + IN AML_NODE_HANDLE OldNode, + IN AML_NODE_HANDLE NewNode + ); + +/** + @defgroup NodeInterfaceApis Node Interface APIs + @ingroup AMLLib + @{ + Node Interface APIs allow to query information from a node. Some functions + expect a specific node type among the root/object/data node types. + + For instance, AmlGetRootNodeInfo expects to receive a root node. + + E.g.: Query the node type, the ACPI header stored in the root node, + the OpCode/SubOpCode/PkgLen of an object node, the type of data + stored in a data node, etc. + + These APIs also allow to update some information. + + E.g.: The ACPI header stored in the root node, the buffer of a data node. + + The information of object nodes and the data type of data nodes cannot be + modified. This prevents the creation of an inconsistent tree. + + It is however possible to remove a node from a variable list of arguments + and replace it. Use the @ref TreeModificationApis APIs for this. + @} +*/ + +/** Returns the tree node type (Root/Object/Data). + + @ingroup NodeInterfaceApis + + @param [in] Node Pointer to a Node. + + @return The node type. + EAmlNodeUnknown if invalid parameter. +**/ +EAML_NODE_TYPE +EFIAPI +AmlGetNodeType ( + IN AML_NODE_HANDLE Node + ); + +/** Get the RootNode information. + The Node must be a root node. + + @ingroup NodeInterfaceApis + + @param [in] RootNode Pointer to a root node. + @param [out] SdtHeaderBuffer Buffer to copy the ACPI DSDT/SSDT header to. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetRootNodeInfo ( + IN AML_ROOT_NODE_HANDLE RootNode, + OUT EFI_ACPI_DESCRIPTION_HEADER * SdtHeaderBuffer + ); + +/** Get the ObjectNode information. + The Node must be an object node. + + @ingroup NodeInterfaceApis + + @param [in] ObjectNode Pointer to an object node. + @param [out] OpCode Pointer holding the OpCode. + Optional, can be NULL. + @param [out] SubOpCode Pointer holding the SubOpCode. + Optional, can be NULL. + @param [out] PkgLen Pointer holding the PkgLen. + The PkgLen is 0 for nodes + not having the Pkglen attribute. + Optional, can be NULL. + @param [out] IsNameSpaceNode Pointer holding TRUE if the node is defining + or changing the NameSpace scope. + E.g.: The "Name ()" and "Scope ()" ASL + statements add/modify the NameSpace scope. + Their corresponding node are NameSpace nodes. + Optional, can be NULL. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetObjectNodeInfo ( + IN AML_OBJECT_NODE_HANDLE ObjectNode, + OUT UINT8 * OpCode, OPTIONAL + OUT UINT8 * SubOpCode, OPTIONAL + OUT UINT32 * PkgLen, OPTIONAL + OUT BOOLEAN * IsNameSpaceNode OPTIONAL + ); + +/** Returns the count of the fixed arguments for the input Node. + + @ingroup NodeInterfaceApis + + @param [in] Node Pointer to an object node. + + @return Number of fixed arguments of the object node. + Return 0 if the node is not an object node. +**/ +UINT8 +AmlGetFixedArgumentCount ( + IN AML_OBJECT_NODE_HANDLE Node + ); + +/** Get the data type of the DataNode. + The Node must be a data node. + + @ingroup NodeInterfaceApis + + @param [in] DataNode Pointer to a data node. + @param [out] DataType Pointer holding the data type of the data buffer. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetNodeDataType ( + IN AML_DATA_NODE_HANDLE DataNode, + OUT EAML_NODE_DATA_TYPE * DataType + ); + +/** Get the descriptor Id of the resource data element + contained in the DataNode. + + The Node must be a data node. + The Node must have the resource data type, i.e. have the + EAmlNodeDataTypeResourceData data type. + + @ingroup NodeInterfaceApis + + @param [in] DataNode Pointer to a data node containing a + resource data element. + @param [out] ResourceDataType Pointer holding the descriptor Id of + the resource data. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetResourceDataType ( + IN AML_DATA_NODE_HANDLE DataNode, + OUT AML_RD_HEADER * ResourceDataType + ); + +/** Get the data buffer and size of the DataNode. + The Node must be a data node. + + BufferSize is always updated to the size of buffer of the DataNode. + + If: + - the content of BufferSize is >= to the DataNode's buffer size; + - Buffer is not NULL; + then copy the content of the DataNode's buffer in Buffer. + + @ingroup NodeInterfaceApis + + @param [in] DataNode Pointer to a data node. + @param [out] Buffer Buffer to write the data to. + Optional, if NULL, only update BufferSize. + @param [in, out] BufferSize Pointer holding: + - At entry, the size of the Buffer; + - At exit, the size of the DataNode's + buffer size. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetDataNodeBuffer ( + IN AML_DATA_NODE_HANDLE DataNode, + OUT UINT8 * Buffer, OPTIONAL + IN OUT UINT32 * BufferSize + ); + +/** Update the ACPI DSDT/SSDT table header. + + The input SdtHeader information is copied to the tree RootNode. + The table Length field is automatically updated. + The checksum field is only updated when serializing the tree. + + @ingroup NodeInterfaceApis + + @param [in] RootNode Pointer to a root node. + @param [in] SdtHeader Pointer to an ACPI DSDT/SSDT table header. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRootNode ( + IN AML_ROOT_NODE_HANDLE RootNode, + IN CONST EFI_ACPI_DESCRIPTION_HEADER * SdtHeader + ); + +/** Update an object node representing an integer with a new value. + + The object node must have one of the following OpCodes: + - AML_BYTE_PREFIX + - AML_WORD_PREFIX + - AML_DWORD_PREFIX + - AML_QWORD_PREFIX + - AML_ZERO_OP + - AML_ONE_OP + + The following OpCode is not supported: + - AML_ONES_OP + + @param [in] IntegerOpNode Pointer an object node containing an integer. + Must not be an object node with an AML_ONES_OP + OpCode. + @param [in] NewInteger New integer value to set. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlUpdateInteger ( + IN AML_OBJECT_NODE_HANDLE IntegerOpNode, + IN UINT64 NewInteger + ); + +/** Update the buffer of a data node. + + Note: The data type of the buffer's content must match the data type of the + DataNode. This is a hard restriction to prevent undesired behaviour. + + @ingroup NodeInterfaceApis + + @param [in] DataNode Pointer to a data node. + @param [in] DataType Data type of the Buffer's content. + @param [in] Buffer Buffer containing the new data. The content of + the Buffer is copied. + @param [in] Size Size of the Buffer. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_UNSUPPORTED Operation not supporter. +**/ +EFI_STATUS +EFIAPI +AmlUpdateDataNode ( + IN AML_DATA_NODE_HANDLE DataNode, + IN EAML_NODE_DATA_TYPE DataType, + IN UINT8 * Buffer, + IN UINT32 Size + ); + +/** + @defgroup NavigationApis Navigation APIs + @ingroup AMLLib + @{ + Navigation APIs allow to navigate in the AML tree. There are different + ways to navigate in the tree by: + - Direct relation (@ref CoreNavigationApis); + - Enumeration: enumerate all the nodes and call a callback function + (@ref EnumerationApis); + - Iteration: instantiate an iterator and use it to navigate + (@ref IteratorApis); + - NameSpace path: use the AML namespace to navigate the tree + (@ref NameSpaceApis). + @} +*/ + +/** + @defgroup CoreNavigationApis Core Navigation APIs + @ingroup NavigationApis + @{ + Core Navigation APIs allow to get a node by specifying a relation. + + E.g.: Get the parent, the n-th fixed argument, the next variable + argument, etc. + @} +*/ + +/** Get the parent node of the input Node. + + @ingroup CoreNavigationApis + + @param [in] Node Pointer to a node. + + @return The parent node of the input Node. + NULL otherwise. +**/ +AML_NODE_HANDLE +EFIAPI +AmlGetParent ( + IN AML_NODE_HANDLE Node + ); + +/** Get the node at the input Index in the fixed argument list of the input + ObjectNode. + + @ingroup CoreNavigationApis + + @param [in] ObjectNode Pointer to an object node. + @param [in] Index The Index of the fixed argument to get. + + @return The node at the input Index in the fixed argument list + of the input ObjectNode. + NULL otherwise, e.g. if the node is not an object node, or no + node is available at this Index. +**/ +AML_NODE_HANDLE +EFIAPI +AmlGetFixedArgument ( + IN AML_OBJECT_NODE_HANDLE ObjectNode, + IN EAML_PARSE_INDEX Index + ); + +/** Get the sibling node among the nodes being in + the same variable argument list. + + (ParentNode) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(VarArgNode)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Node must be in a variable list of arguments. + Traversal Order: VarArgNode, f, g, NULL + + @ingroup CoreNavigationApis + + @param [in] VarArgNode Pointer to a node. + Must be in a variable list of arguments. + + @return The next node after VarArgNode in the variable list of arguments. + Return NULL if + - VarArgNode is the last node of the list, or + - VarArgNode is not part of a variable list of arguments. +**/ +AML_NODE_HANDLE +EFIAPI +AmlGetSiblingVariableArgument ( + IN AML_NODE_HANDLE VarArgNode + ); + +/** Get the next variable argument. + + (Node) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(e)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Traversal Order: e, f, g, NULL + + @ingroup CoreNavigationApis + + @param [in] Node Pointer to a Root node or Object Node. + @param [in] CurrVarArg Pointer to the Current Variable Argument. + + @return The node after the CurrVarArg in the variable list of arguments. + If CurrVarArg is NULL, return the first node of the + variable argument list. + Return NULL if + - CurrVarArg is the last node of the list, or + - Node does not have a variable list of arguments. +**/ +AML_NODE_HANDLE +EFIAPI +AmlGetNextVariableArgument ( + IN AML_NODE_HANDLE Node, + IN AML_NODE_HANDLE CurrVarArg + ); + +/** Get the previous variable argument. + + (Node) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(e)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Traversal Order: g, f, e, NULL + + @ingroup CoreNavigationApis + + @param [in] Node Pointer to a root node or an object node. + @param [in] CurrVarArg Pointer to the Current Variable Argument. + + @return The node before the CurrVarArg in the variable list of + arguments. + If CurrVarArg is NULL, return the last node of the + variable list of arguments. + Return NULL if: + - CurrVarArg is the first node of the list, or + - Node doesn't have a variable list of arguments. +**/ +AML_NODE_HANDLE +EFIAPI +AmlGetPreviousVariableArgument ( + IN AML_NODE_HANDLE Node, + IN AML_NODE_HANDLE CurrVarArg + ); + +/** + @defgroup EnumerationApis Enumeration APIs + @ingroup NavigationApis + @{ + Enumeration APIs are navigation APIs, allowing to call a callback function + on each node enumerated. Nodes are enumerated in the AML bytestream order, + i.e. in a depth first order. + @} +*/ + +/** + Callback function prototype used when iterating through the tree. + + @ingroup EnumerationApis + + @param [in] Node The Node currently being processed. + @param [in, out] Context A context for the callback function. + Can be optional. + @param [in, out] Status End the enumeration if pointing to a value + evaluated to TRUE. + Can be optional. + + @retval TRUE if the enumeration can continue or has finished without + interruption. + @retval FALSE if the enumeration needs to stopped or has stopped. +**/ +typedef +BOOLEAN +(EFIAPI * EDKII_AML_TREE_ENUM_CALLBACK) ( + IN AML_NODE_HANDLE Node, + IN OUT VOID * Context, OPTIONAL + IN OUT EFI_STATUS * Status OPTIONAL + ); + +/** Enumerate all nodes of the subtree under the input Node in the AML + bytestream order (i.e. in a depth first order), and call the CallBack + function with the input Context. + The prototype of the Callback function is EDKII_AML_TREE_ENUM_CALLBACK. + + @ingroup EnumerationApis + + @param [in] Node Enumerate nodes of the subtree under this Node. + Must be a valid node. + @param [in] CallBack Callback function to call on each node. + @param [in, out] Context Void pointer used to pass some information + to the Callback function. + Optional, can be NULL. + @param [out] Status Optional parameter that can be used to get + the status of the Callback function. + If used, need to be init to EFI_SUCCESS. + + @retval TRUE if the enumeration can continue or has finished without + interruption. + @retval FALSE if the enumeration needs to stopped or has stopped. +**/ +BOOLEAN +EFIAPI +AmlEnumTree ( + IN AML_NODE_HANDLE Node, + IN EDKII_AML_TREE_ENUM_CALLBACK CallBack, + IN OUT VOID * Context, OPTIONAL + OUT EFI_STATUS * Status OPTIONAL + ); + +/** + @defgroup NameSpaceApis NameSpace APIs + @ingroup NavigationApis + @{ + NameSpace APIs allow to find a node from an AML path, and reciprocally + get the AML path of a node. + + These APIs only operate on "NameSpace nodes", i.e. nodes that are + part of the AML namespace. These are the root node and object nodes + acknowledged by AmlGetObjectNodeInfo in @ref NodeInterfaceApis. + @} +*/ + +/** 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. + + @ingroup NameSpaceApis + + @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_HANDLE Node, + OUT CHAR8 * Buffer, + IN OUT UINT32 * BufferSize + ); + +#endif // AML_CORE_INTERFACE_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c new file mode 100644 index 000000000..7b11cc8e9 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.c @@ -0,0 +1,546 @@ +/** @file + AML Print Function. + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include + +#include +#include +#include +#include + +#if !defined (MDEPKG_NDEBUG) + +/** String table representing AML Data types as defined by EAML_NODE_DATA_TYPE. +*/ +CONST CHAR8 * NodeDataTypeStrTbl[] = { + "EAmlNodeDataTypeNone", + "EAmlNodeDataTypeReserved1", + "EAmlNodeDataTypeReserved2", + "EAmlNodeDataTypeReserved3", + "EAmlNodeDataTypeReserved4", + "EAmlNodeDataTypeReserved5", + "EAmlNodeDataTypeNameString", + "EAmlNodeDataTypeString", + "EAmlNodeDataTypeUInt", + "EAmlNodeDataTypeRaw", + "EAmlNodeDataTypeResourceData", + "EAmlNodeDataTypeFieldPkgLen", + "EAmlNodeDataTypeMax" +}; + +/** String table representing AML Node types as defined by EAML_NODE_TYPE. +*/ +CONST CHAR8 * NodeTypeStrTbl[] = { + "EAmlNodeUnknown", + "EAmlNodeRoot", + "EAmlNodeObject", + "EAmlNodeData", + "EAmlNodeMax" +}; + +/** Print Size chars at Buffer address. + + @param [in] ErrorLevel Error level for the DEBUG macro. + @param [in] Buffer Buffer containing the chars. + @param [in] Size Number of chars to print. +**/ +VOID +EFIAPI +AmlDbgPrintChars ( + IN UINT32 ErrorLevel, + IN CONST CHAR8 * Buffer, + IN UINT32 Size + ) +{ + UINT32 i; + + if (Buffer == NULL) { + ASSERT (0); + return; + } + + for (i = 0; i < Size; i++) { + DEBUG ((ErrorLevel, "%c", Buffer[i])); + } +} + +/** Print an AML NameSeg. + Don't print trailing underscores ('_'). + + @param [in] Buffer Buffer containing an AML NameSeg. +**/ +VOID +EFIAPI +AmlDbgPrintNameSeg ( + IN CONST CHAR8 * Buffer + ) +{ + if (Buffer == NULL) { + ASSERT (0); + return; + } + + DEBUG ((DEBUG_INFO, "%c", Buffer[0])); + if ((Buffer[1] == AML_NAME_CHAR__) && + (Buffer[2] == AML_NAME_CHAR__) && + (Buffer[3] == AML_NAME_CHAR__)) { + return; + } + DEBUG ((DEBUG_INFO, "%c", Buffer[1])); + if ((Buffer[2] == AML_NAME_CHAR__) && + (Buffer[3] == AML_NAME_CHAR__)) { + return; + } + DEBUG ((DEBUG_INFO, "%c", Buffer[2])); + if (Buffer[3] == AML_NAME_CHAR__) { + return; + } + DEBUG ((DEBUG_INFO, "%c", Buffer[3])); + return; +} + +/** Print an AML NameString. + + @param [in] Buffer Buffer containing an AML NameString. + @param [in] NewLine Print a newline char at the end of the NameString. +**/ +VOID +EFIAPI +AmlDbgPrintNameString ( + IN CONST CHAR8 * Buffer, + IN BOOLEAN NewLine + ) +{ + UINT8 SegCount; + UINT8 Index; + + if (Buffer == NULL) { + ASSERT (0); + return; + } + + // Handle Root and Parent(s). + if (*Buffer == AML_ROOT_CHAR) { + Buffer++; + DEBUG ((DEBUG_INFO, "\\")); + } else if (*Buffer == AML_PARENT_PREFIX_CHAR) { + do { + Buffer++; + DEBUG ((DEBUG_INFO, "^")); + } while (*Buffer == AML_PARENT_PREFIX_CHAR); + } + + // Handle SegCount(s). + if (*Buffer == AML_DUAL_NAME_PREFIX) { + Buffer++; + SegCount = 2; + } else if (*Buffer == AML_MULTI_NAME_PREFIX) { + Buffer++; + // For multi name prefix the seg count is in the second byte. + SegCount = *Buffer; + Buffer++; + } else if (AmlIsLeadNameChar (*Buffer)) { + // Only check the first char first to avoid overflow. + // Then the whole NameSeg can be checked. + if (!AmlIsNameSeg (Buffer)) { + ASSERT (0); + return; + } + SegCount = 1; + } else if (*Buffer == AML_ZERO_OP) { + SegCount = 0; + } else { + // Should not be possible. + ASSERT (0); + return; + } + + if (SegCount != 0) { + AmlDbgPrintNameSeg (Buffer); + Buffer += AML_NAME_SEG_SIZE; + for (Index = 0; Index < SegCount - 1; Index++) { + DEBUG ((DEBUG_INFO, ".")); + AmlDbgPrintNameSeg (Buffer); + Buffer += AML_NAME_SEG_SIZE; + } + } + + if (NewLine) { + DEBUG ((DEBUG_INFO, "\n")); + } + + return; +} + +/** Print the information contained in the header of the Node. + + @param [in] Node Pointer to a node. + @param [in] Level Level of the indentation. +**/ +STATIC +VOID +EFIAPI +AmlDbgPrintNodeHeader ( + IN AML_NODE_HEADER * Node, + IN UINT8 Level + ) +{ + if (!IS_AML_NODE_VALID (Node)) { + ASSERT (0); + return; + } + + DEBUG (( + DEBUG_INFO, + "%3d | %-15s | ", + Level, + NodeTypeStrTbl[Node->NodeType] + )); +} + +/** Print fields of a data node. + + @param [in] DataNode Pointer to a data node. + @param [in] Level Level of the indentation. +**/ +STATIC +VOID +EFIAPI +AmlDbgPrintDataNode ( + IN AML_DATA_NODE * DataNode, + IN UINT8 Level + ) +{ + UINT32 Idx; + + if (!IS_AML_DATA_NODE (DataNode)) { + ASSERT (0); + return; + } + + AmlDbgPrintNodeHeader ((AML_NODE_HEADER*)DataNode, Level); + + DEBUG ((DEBUG_INFO, "%-36s | ", NodeDataTypeStrTbl[DataNode->DataType])); + DEBUG ((DEBUG_INFO, "0x%04x | ", DataNode->Size)); + + if ((DataNode->DataType == EAmlNodeDataTypeNameString) || + (DataNode->DataType == EAmlNodeDataTypeString)) { + AmlDbgPrintChars ( + DEBUG_INFO, + (CONST CHAR8*)DataNode->Buffer, + DataNode->Size + ); + } else if (DataNode->DataType == EAmlNodeDataTypeUInt) { + switch (DataNode->Size) { + case 1: + { + DEBUG ((DEBUG_INFO, "0x%0x", *((UINT8*)DataNode->Buffer))); + break; + } + case 2: + { + DEBUG ((DEBUG_INFO, "0x%0x", *((UINT16*)DataNode->Buffer))); + break; + } + case 4: + { + DEBUG ((DEBUG_INFO, "0x%0lx", *((UINT32*)DataNode->Buffer))); + break; + } + case 8: + { + DEBUG ((DEBUG_INFO, "0x%0llx", *((UINT64*)DataNode->Buffer))); + break; + } + default: + { + ASSERT (0); + return; + } + } + } else { + // No specific format. + for (Idx = 0; Idx < DataNode->Size; Idx++) { + DEBUG ((DEBUG_INFO, "%02x ", DataNode->Buffer[Idx])); + } + } + + DEBUG ((DEBUG_INFO, "\n")); +} + +/** Print fields of an object node. + + @param [in] ObjectNode Pointer to an object node. + @param [in] Level Level of the indentation. +**/ +STATIC +VOID +EFIAPI +AmlDbgPrintObjectNode ( + IN AML_OBJECT_NODE * ObjectNode, + IN UINT8 Level + ) +{ + if (!IS_AML_OBJECT_NODE (ObjectNode)) { + ASSERT (0); + return; + } + + AmlDbgPrintNodeHeader ((AML_NODE_HEADER*)ObjectNode, Level); + + DEBUG ((DEBUG_INFO, "0x%02x | ", ObjectNode->AmlByteEncoding->OpCode)); + DEBUG ((DEBUG_INFO, "0x%02x | ", ObjectNode->AmlByteEncoding->SubOpCode)); + + // Print a string corresponding to the field object OpCode/SubOpCode. + if (AmlNodeHasAttribute (ObjectNode, AML_IS_FIELD_ELEMENT)) { + DEBUG ((DEBUG_INFO, "%-15s ", AmlGetFieldOpCodeStr ( + ObjectNode->AmlByteEncoding->OpCode, + 0 + ))); + } else { + // Print a string corresponding to the object OpCode/SubOpCode. + DEBUG ((DEBUG_INFO, "%-15s | ", AmlGetOpCodeStr ( + ObjectNode->AmlByteEncoding->OpCode, + ObjectNode->AmlByteEncoding->SubOpCode) + )); + } + + DEBUG ((DEBUG_INFO, "%3d | ", ObjectNode->AmlByteEncoding->MaxIndex)); + DEBUG ((DEBUG_INFO, "0x%08x | ", ObjectNode->AmlByteEncoding->Attribute)); + DEBUG ((DEBUG_INFO, "0x%04x | ", ObjectNode->PkgLen)); + if (AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE)) { + AmlDbgPrintNameString ( + AmlNodeGetName ((CONST AML_OBJECT_NODE*)ObjectNode), + FALSE + ); + } + + DEBUG ((DEBUG_INFO, "\n")); +} + +/** Print fields of a root node. + + @param [in] RootNode Pointer to a root node. + @param [in] Level Level of the indentation. +**/ +STATIC +VOID +EFIAPI +AmlDbgPrintRootNode ( + IN AML_ROOT_NODE * RootNode, + IN UINT8 Level + ) +{ + if (!IS_AML_ROOT_NODE (RootNode)) { + ASSERT (0); + return; + } + + AmlDbgPrintNodeHeader ((AML_NODE_HEADER*)RootNode, Level); + + DEBUG ((DEBUG_INFO, "%8x | ", RootNode->SdtHeader->Signature)); + DEBUG ((DEBUG_INFO, "0x%08x | ", RootNode->SdtHeader->Length)); + DEBUG ((DEBUG_INFO, "%3d | ", RootNode->SdtHeader->Revision)); + DEBUG ((DEBUG_INFO, "0x%02x | ", RootNode->SdtHeader->Checksum)); + DEBUG (( + DEBUG_INFO, + "%c%c%c%c%c%c | ", + RootNode->SdtHeader->OemId[0], + RootNode->SdtHeader->OemId[1], + RootNode->SdtHeader->OemId[2], + RootNode->SdtHeader->OemId[3], + RootNode->SdtHeader->OemId[4], + RootNode->SdtHeader->OemId[5] + )); + DEBUG ((DEBUG_INFO, "%-16llx | ", RootNode->SdtHeader->OemTableId)); + DEBUG ((DEBUG_INFO, "%8x | ", RootNode->SdtHeader->OemRevision)); + DEBUG ((DEBUG_INFO, "%8x | ", RootNode->SdtHeader->CreatorId)); + DEBUG ((DEBUG_INFO, "%8x", RootNode->SdtHeader->CreatorRevision)); + DEBUG ((DEBUG_INFO, "\n")); +} + +/** Print a header to help interpreting node information. +**/ +STATIC +VOID +EFIAPI +AmlDbgPrintTableHeader ( + VOID + ) +{ + DEBUG ((DEBUG_INFO, "Lvl | Node Type |\n")); + DEBUG (( + DEBUG_INFO, + " | %-15s | Signature| Length | Rev | CSum | OemId | " + "OemTableId | OemRev | CreatorId| CreatorRev\n", + NodeTypeStrTbl[EAmlNodeRoot] + )); + DEBUG (( + DEBUG_INFO, + " | %-15s | Op | SubOp| OpName | MaxI| Attribute | " + "PkgLen | NodeName (opt)\n", + NodeTypeStrTbl[EAmlNodeObject] + )); + DEBUG (( + DEBUG_INFO, + " | %-15s | Data Type | Size | " + "Buffer\n", + NodeTypeStrTbl[EAmlNodeData] + )); + DEBUG (( + DEBUG_INFO, + "---------------------------------------" + "---------------------------------------\n" + )); +} + +/** Recursively print the subtree under the Node. + This is an internal function. + + @param [in] Node Pointer to the root of the subtree to print. + Can be a root/object/data node. + @param [in] Recurse If TRUE, recurse. + @param [in] Level Level in the tree. +**/ +STATIC +VOID +EFIAPI +AmlDbgPrintTreeInternal ( + IN AML_NODE_HEADER * Node, + IN BOOLEAN Recurse, + IN UINT8 Level + ) +{ + AML_NODE_HEADER * ChildNode; + + if (!IS_AML_NODE_VALID (Node)) { + ASSERT (0); + return; + } + + if (IS_AML_DATA_NODE (Node)) { + AmlDbgPrintDataNode ((AML_DATA_NODE*)Node, Level); + return; + } else if (IS_AML_OBJECT_NODE (Node)) { + AmlDbgPrintObjectNode ((AML_OBJECT_NODE*)Node, Level); + } else if (IS_AML_ROOT_NODE (Node)) { + AmlDbgPrintRootNode ((AML_ROOT_NODE*)Node, Level); + } else { + // Should not be possible. + ASSERT (0); + return; + } + + if (!Recurse) { + return; + } + + // Get the first child node. + ChildNode = AmlGetNextSibling (Node, NULL); + while (ChildNode != NULL) { + ASSERT (Level < MAX_UINT8); + AmlDbgPrintTreeInternal (ChildNode, Recurse, (UINT8)(Level + 1)); + ChildNode = AmlGetNextSibling (Node, ChildNode); + } +} + +/** Print Node information. + + @param [in] Node Pointer to the Node to print. + Can be a root/object/data node. +**/ +VOID +EFIAPI +AmlDbgPrintNode ( + IN AML_NODE_HEADER * Node + ) +{ + AmlDbgPrintTableHeader (); + AmlDbgPrintTreeInternal (Node, FALSE, 0); +} + +/** Recursively print the subtree under the Node. + + @param [in] Node Pointer to the root of the subtree to print. + Can be a root/object/data node. +**/ +VOID +EFIAPI +AmlDbgPrintTree ( + IN AML_NODE_HEADER * Node + ) +{ + AmlDbgPrintTableHeader (); + AmlDbgPrintTreeInternal (Node, TRUE, 0); +} + +/** This function performs a raw data dump of the ACPI table. + + @param [in] Ptr Pointer to the start of the table buffer. + @param [in] Length The length of the buffer. +**/ +VOID +EFIAPI +DumpRaw ( + IN CONST UINT8 * Ptr, + IN UINT32 Length + ) +{ + UINT32 ByteCount; + UINT32 PartLineChars; + UINT32 AsciiBufferIndex; + CHAR8 AsciiBuffer[17]; + + ByteCount = 0; + AsciiBufferIndex = 0; + + DEBUG ((DEBUG_VERBOSE, "Address : 0x%p\n", Ptr)); + DEBUG ((DEBUG_VERBOSE, "Length : %lld", Length)); + + while (ByteCount < Length) { + if ((ByteCount & 0x0F) == 0) { + AsciiBuffer[AsciiBufferIndex] = '\0'; + DEBUG ((DEBUG_VERBOSE, " %a\n%08X : ", AsciiBuffer, ByteCount)); + AsciiBufferIndex = 0; + } else if ((ByteCount & 0x07) == 0) { + DEBUG ((DEBUG_VERBOSE, "- ")); + } + + if ((*Ptr >= ' ') && (*Ptr < 0x7F)) { + AsciiBuffer[AsciiBufferIndex++] = *Ptr; + } else { + AsciiBuffer[AsciiBufferIndex++] = '.'; + } + + DEBUG ((DEBUG_VERBOSE, "%02X ", *Ptr++)); + + ByteCount++; + } + + // Justify the final line using spaces before printing + // the ASCII data. + PartLineChars = (Length & 0x0F); + if (PartLineChars != 0) { + PartLineChars = 48 - (PartLineChars * 3); + if ((Length & 0x0F) <= 8) { + PartLineChars += 2; + } + while (PartLineChars > 0) { + DEBUG ((DEBUG_VERBOSE, " ")); + PartLineChars--; + } + } + + // Print ASCII data for the final line. + AsciiBuffer[AsciiBufferIndex] = '\0'; + DEBUG ((DEBUG_VERBOSE, " %a\n\n", AsciiBuffer)); +} + +#endif // MDEPKG_NDEBUG diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h new file mode 100644 index 000000000..68f4c7416 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlDbgPrint/AmlDbgPrint.h @@ -0,0 +1,154 @@ +/** @file + AML Debug Print. + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_PRINT_H_ +#define AML_PRINT_H_ + +/* This header file does not include internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. The node definitions + must be included by the caller file. The function prototypes must + only expose AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, etc. node + definitions. + This allows to keep the functions defined here both internal and + potentially external. If necessary, any function of this file can + be exposed externally. + The Api folder is internal to the AmlLib, but should only use these + functions. They provide a "safe" way to interact with the AmlLib. +*/ + +#if !defined (MDEPKG_NDEBUG) + +#include + +/** + @defgroup DbgPrintApis Print APIs for debugging. + @ingroup AMLLib + @{ + Print APIs provide a way to print: + - A buffer; + - A (root/object/data) node; + - An AML tree/branch; + - The AML NameSpace from the root node. + @} +*/ + +/** This function performs a raw data dump of the ACPI table. + + @param [in] Ptr Pointer to the start of the table buffer. + @param [in] Length The length of the buffer. +**/ +VOID +EFIAPI +DumpRaw ( + IN CONST UINT8 * Ptr, + IN UINT32 Length + ); + +/** Print Size chars at Buffer address. + + @ingroup DbgPrintApis + + @param [in] ErrorLevel Error level for the DEBUG macro. + @param [in] Buffer Buffer containing the chars. + @param [in] Size Number of chars to print. +**/ +VOID +EFIAPI +AmlDbgPrintChars ( + IN UINT32 ErrorLevel, + IN CONST CHAR8 * Buffer, + IN UINT32 Size + ); + +/** Print an AML NameSeg. + Don't print trailing underscores ('_'). + + @param [in] Buffer Buffer containing an AML NameSeg. +**/ +VOID +EFIAPI +AmlDbgPrintNameSeg ( + IN CONST CHAR8 * Buffer + ); + +/** Print an AML NameString. + + @param [in] Buffer Buffer containing an AML NameString. + @param [in] NewLine Print a newline char at the end of the NameString. +**/ +VOID +EFIAPI +AmlDbgPrintNameString ( + IN CONST CHAR8 * Buffer, + IN BOOLEAN NewLine + ); + +/** Print Node information. + + @ingroup DbgPrintApis + + @param [in] Node Pointer to the Node to print. + Can be a root/object/data node. +**/ +VOID +EFIAPI +AmlDbgPrintNode ( + IN AML_NODE_HANDLE Node + ); + +/** Recursively print the subtree under the Node. + + @ingroup DbgPrintApis + + @param [in] Node Pointer to the root of the subtree to print. + Can be a root/object/data node. +**/ +VOID +EFIAPI +AmlDbgPrintTree ( + IN AML_NODE_HANDLE Node + ); + +/** Print the absolute pathnames in the AML namespace of + all the nodes in the tree starting from the Root node. + + @ingroup DbgPrintApis + + @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_HANDLE RootNode + ); + +#else + +#define DumpRaw(Ptr, Length) + +#define AmlDbgPrintChars(ErrorLevel, Buffer, Size) + +#define AmlDbgPrintNameSeg(Buffer) + +#define AmlDbgPrintNameString(Buffer,NewLine) + +#define AmlDbgPrintNode(Node) + +#define AmlDbgPrintTree(Node) + +#define AmlDbgPrintNameSpace(RootNode) + +#endif // MDEPKG_NDEBUG + +#endif // AML_PRINT_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlDefines.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlDefines.h new file mode 100644 index 000000000..cbae14d78 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlDefines.h @@ -0,0 +1,188 @@ +/** @file + AML Defines. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_DEFINES_H_ +#define AML_DEFINES_H_ + +/** + @defgroup TreeStructures Tree structures + @ingroup AMLLib + @{ + The AML tree created by the AMLLib relies on enum/define values and + structures defined here. + @} +*/ + +/** AML tree node types. + + Data nodes are tagged with the data type they contain. + Some data types cannot be used for data nodes (None, Object). + EAmlUIntX types are converted to the EAML_NODE_DATA_TYPE enum type. + These types are accessible externally. + + @ingroup TreeStructures +*/ +typedef enum EAmlNodeDataType { + EAmlNodeDataTypeNone = 0, ///< EAmlNone, not accessible. + EAmlNodeDataTypeReserved1, ///< EAmlUInt8, converted to the UInt type. + EAmlNodeDataTypeReserved2, ///< EAmlUInt16, converted to the UInt type. + EAmlNodeDataTypeReserved3, ///< EAmlUInt32, converted to the UInt type. + EAmlNodeDataTypeReserved4, ///< EAmlUInt64, converted to the UInt type. + EAmlNodeDataTypeReserved5, ///< EAmlObject, not accessible. + EAmlNodeDataTypeNameString, ///< EAmlName, name corresponding to the + /// NameString keyword in the ACPI + /// specification. E.g.: "\_SB_.DEV0" + EAmlNodeDataTypeString, ///< EAmlString, NULL terminated string. + EAmlNodeDataTypeUInt, ///< Integer data of any length, EAmlUIntX + /// are converted to this type. + EAmlNodeDataTypeRaw, ///< Raw bytes contained in a buffer. + EAmlNodeDataTypeResourceData, ///< Resource data element. + EAmlNodeDataTypeFieldPkgLen, ///< FieldPkgLen data element. + /// PkgLen are usually stored as + /// part of object node structures. + /// However, they can be found + /// standalone in a FieldList. + EAmlNodeDataTypeMax ///< Max enum. +} EAML_NODE_DATA_TYPE; + +/** Indexes of fixed arguments. + + AML objects defined the ACPI 6.3 specification, + s20.3 "AML Byte Stream Byte Values" can have at most 6 fixed arguments. + + Method and functions can have at most 7 arguments, cf + s19.6.83 "Method (Declare Control Method)". The enum goes to 8 to store the + name of the method invocation. + + @ingroup TreeStructures +*/ +typedef enum EAmlParseIndex { + EAmlParseIndexTerm0 = 0, ///< First fixed argument index. + EAmlParseIndexTerm1, ///< Second fixed argument index. + EAmlParseIndexTerm2, ///< Third fixed argument index. + EAmlParseIndexTerm3, ///< Fourth fixed argument index. + EAmlParseIndexTerm4, ///< Fifth fixed argument index. + EAmlParseIndexTerm5, ///< Sixth fixed argument index. + EAmlParseIndexMax ///< Maximum fixed argument index (=6). +} EAML_PARSE_INDEX; + +/** Maximum size of an AML NameString. + + An AML NameString can be at most (255 * 4) + 255 + 2 = 1277 bytes long. + Indeed, according to ACPI 6.3 specification, s20.2.2, + an AML NameString can be resolved as a MultiNamePath. + + The encoding of this MultiNamePath can be made of at most: + - 255 carets ('^'), one for each level in the namespace; + - 255 NameSeg of 4 bytes; + - 2 bytes for the MultiNamePrefix and SegCount. + + @ingroup TreeStructures +*/ +#define MAX_AML_NAMESTRING_SIZE 1277U + +/** Maximum size of an ASL NameString. + + An ASL NameString can be at most (255 * 4) + 255 + 254 = 1529 bytes long. + Cf the ASL grammar available in ACPI 6.3 specification, 19.2.2. + + The encoding of an ASL NameString can be made of at most: + - 255 carets ('^'), one for each level in the namespace; + - 255 NameSeg of 4 bytes; + - 254 NameSeg separators ('.'). + + @ingroup TreeStructures +*/ +#define MAX_ASL_NAMESTRING_SIZE 1529U + +/** Pseudo OpCode for method invocations. + + 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). + + @ingroup TreeStructures +*/ +#define AML_METHOD_INVOC_OP 0xD0 + +/** Pseudo OpCode for NamedField field elements. + + The AML grammar does not attribute an OpCode/SubOpCode couple for + the NamedField field element. This library is representing NamedField field + elements as if they had one. + + The AML encoding for NamedField field elements in the ACPI specification 6.3 + is: + NamedField := NameSeg PkgLength + In this library, it is: + NamedField := NamedFieldOp NameSeg PkgLength + + When computing the size of a tree or serializing it, the additional data is + not taken into account (i.e. the NamedFieldOp). + + @ingroup TreeStructures +*/ +#define AML_FIELD_NAMED_OP 0x04 + +/** Size of a NameSeg. + Cf. ACPI 6.3 specification, s20.2. + + @ingroup TreeStructures +*/ + #define AML_NAME_SEG_SIZE 4U + +/** AML object types. + + The ACPI specification defines several object types. They are listed + with the definition of ObjectTypeKeyword. + + @ingroup TreeStructures +*/ +typedef enum EAmlObjType { + EAmlObjTypeUnknown = 0x0, + EAmlObjTypeInt, + EAmlObjTypeStrObj, + EAmlObjTypeBuffObj, + EAmlObjTypePkgObj, + EAmlObjTypeFieldUnitObj, + EAmlObjTypeDeviceObj, + EAmlObjTypeEventObj, + EAmlObjTypeMethodObj, + EAmlObjTypeMutexObj, + EAmlObjTypeOpRegionObj, + EAmlObjTypePowerResObj, + EAmlObjTypeProcessorObj, + EAmlObjTypeThermalZoneObj, + EAmlObjTypeBuffFieldObj, + EAmlObjTypeDDBHandleObj, +} EAML_OBJ_TYPE; + +/** Node types. + + @ingroup TreeStructures +*/ +typedef enum EAmlNodeType { + EAmlNodeUnknown, ///< Unknown/Invalid AML Node Type. + EAmlNodeRoot, ///< AML Root Node, typically represents a DefinitionBlock. + EAmlNodeObject, ///< AML Object Node, typically represents an ASL statement + /// or its arguments. + EAmlNodeData, ///< AML Data Node, typically represents arguments for an + /// ASL statement. + EAmlNodeMax ///< Max enum. +} EAML_NODE_TYPE; + +#endif // AML_DEFINES_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.c new file mode 100644 index 000000000..780bdea67 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.c @@ -0,0 +1,805 @@ +/** @file + AML grammar definitions. + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +/** AML grammar encoding table. + + The ASL language is a description language, used to define abstract + objects, like devices, thermal zones, etc. and their place in a hierarchical + tree. The following table stores the AML grammar definition. It can be used + to parse an AML bytestream. Each line corresponds to the definition of an + opcode and what is expected to be found with this opcode. + See table 20-440 in the ACPI 6.3 specification s20.3, and the AML + grammar definitions in s20.2. + + - OpCode/SubOpCode: + An OpCode/SubOpCode couple allows to identify an object type. + The OpCode and SubOpCode are one byte each. The SubOpCode is + used when the Opcode value is 0x5B (extended OpCode). Otherwise + the SubOpcode is set to 0. If the SubOpCode is 0 in the table + below, there is no SubOpCode in the AML bytestream, only the + OpCode is used to identify the object. + + - Fixed arguments: + The fixed arguments follow the OpCode and SubOpCode. Their number + and type can be found in the table below. There can be at the most + 6 fixed arguments for an object. + Fixed arguments's type allow to know what is expected in the AML bytestream. + Knowing the size of the incoming element, AML bytes can be packed and parsed + accordingly. These types can be found in the same table 20-440 in the + ACPI 6.3, s20.3 specification. + E.g.: An AML object, a UINT8, a NULL terminated string, etc. + + -Attributes: + The attribute field gives additional information on each object. This can + be the presence of a variable list of arguments, the presence of a PkgLen, + etc. + + In summary, an AML object is described as: + OpCode [SubOpcode] [PkgLen] [FixedArgs] [VarArgs] + + OpCode {1 byte} + [SubOpCode] {1 byte. + Only relevant if the OpCode value is + 0x5B (extended OpCode prefix). + Otherwise 0. Most objects don't have one.} + [PkgLen] {Size of the object. + It has a special encoding, cf. ACPI 6.3 + specification, s20.2.4 "Package Length + Encoding". + Most objects don't have one.} + [FixedArgs[0..X]] {Fixed list of arguments. + (where X <= 5) Can be other objects or data (a byte, + a string, etc.). They belong to the + current AML object. + The number of fixed arguments varies according + to the object, but it is fixed for each kind of + object.} + [VarArgs] {Variable list of arguments. + They also belong to the current object and can + be objects or data. + Most objects don't have one.} + [ByteList] {This is a sub-type of a variable list of + arguments. It can only be found in buffer + objects. + A ByteList is either a list of bytes or + a list of resource data elements. Resource + data elements have specific opcodes.} + [FieldList] {This is a sub-type of a variable list of + arguments. It can only be found in Fields, + IndexFields and BankFields. + A FieldList is made of FieldElements. + FieldElements have specific opcodes.} +*/ +GLOBAL_REMOVE_IF_UNREFERENCED +STATIC +CONST +AML_BYTE_ENCODING mAmlByteEncoding[] = { + // Comment Str OpCode SubOpCode MaxIndex NameIndex 0 1 2 3 4 5 Attribute + /* 0x00 */ {AML_DEBUG_STR ("ZeroOp") AML_ZERO_OP, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x01 */ {AML_DEBUG_STR ("OneOp") AML_ONE_OP, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x06 */ {AML_DEBUG_STR ("AliasOp") AML_ALIAS_OP, 0, 2, 1, {EAmlName, EAmlName, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE}, + /* 0x08 */ {AML_DEBUG_STR ("NameOp") AML_NAME_OP, 0, 2, 0, {EAmlName, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE}, + /* 0x0A */ {AML_DEBUG_STR ("BytePrefix") AML_BYTE_PREFIX, 0, 1, 0, {EAmlUInt8, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x0B */ {AML_DEBUG_STR ("WordPrefix") AML_WORD_PREFIX, 0, 1, 0, {EAmlUInt16, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x0C */ {AML_DEBUG_STR ("DWordPrefix") AML_DWORD_PREFIX, 0, 1, 0, {EAmlUInt32, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x0D */ {AML_DEBUG_STR ("StringPrefix") AML_STRING_PREFIX, 0, 1, 0, {EAmlString, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x0E */ {AML_DEBUG_STR ("QWordPrefix") AML_QWORD_PREFIX, 0, 1, 0, {EAmlUInt64, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x10 */ {AML_DEBUG_STR ("ScopeOp") AML_SCOPE_OP, 0, 1, 0, {EAmlName, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE}, + /* 0x11 */ {AML_DEBUG_STR ("BufferOp") AML_BUFFER_OP, 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_BYTE_LIST}, + /* 0x12 */ {AML_DEBUG_STR ("PackageOp") AML_PACKAGE_OP, 0, 1, 0, {EAmlUInt8, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ}, + /* 0x13 */ {AML_DEBUG_STR ("VarPackageOp") AML_VAR_PACKAGE_OP, 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ}, + /* 0x14 */ {AML_DEBUG_STR ("MethodOp") AML_METHOD_OP, 0, 2, 0, {EAmlName, EAmlUInt8, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE}, + /* 0x15 */ {AML_DEBUG_STR ("ExternalOp") AML_EXTERNAL_OP, 0, 3, 0, {EAmlName, EAmlUInt8, EAmlUInt8, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE}, + /* 0x2E */ {AML_DEBUG_STR ("DualNamePrefix") AML_DUAL_NAME_PREFIX, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x2F */ {AML_DEBUG_STR ("MultiNamePrefix") AML_MULTI_NAME_PREFIX, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x41 */ {AML_DEBUG_STR ("NameChar_A") 'A', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x42 */ {AML_DEBUG_STR ("NameChar_B") 'B', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x43 */ {AML_DEBUG_STR ("NameChar_C") 'C', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x44 */ {AML_DEBUG_STR ("NameChar_D") 'D', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x45 */ {AML_DEBUG_STR ("NameChar_E") 'E', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x46 */ {AML_DEBUG_STR ("NameChar_F") 'F', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x47 */ {AML_DEBUG_STR ("NameChar_G") 'G', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x48 */ {AML_DEBUG_STR ("NameChar_H") 'H', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x49 */ {AML_DEBUG_STR ("NameChar_I") 'I', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x4A */ {AML_DEBUG_STR ("NameChar_J") 'J', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x4B */ {AML_DEBUG_STR ("NameChar_K") 'K', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x4C */ {AML_DEBUG_STR ("NameChar_L") 'L', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x4D */ {AML_DEBUG_STR ("NameChar_M") 'M', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x4E */ {AML_DEBUG_STR ("NameChar_N") 'N', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x4F */ {AML_DEBUG_STR ("NameChar_O") 'O', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x50 */ {AML_DEBUG_STR ("NameChar_P") 'P', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x51 */ {AML_DEBUG_STR ("NameChar_Q") 'Q', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x52 */ {AML_DEBUG_STR ("NameChar_R") 'R', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x53 */ {AML_DEBUG_STR ("NameChar_S") 'S', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x54 */ {AML_DEBUG_STR ("NameChar_T") 'T', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x55 */ {AML_DEBUG_STR ("NameChar_U") 'U', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x56 */ {AML_DEBUG_STR ("NameChar_V") 'V', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x57 */ {AML_DEBUG_STR ("NameChar_W") 'W', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x58 */ {AML_DEBUG_STR ("NameChar_X") 'X', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x59 */ {AML_DEBUG_STR ("NameChar_Y") 'Y', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x5A */ {AML_DEBUG_STR ("NameChar_Z") 'Z', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x5B 0x01 */ {AML_DEBUG_STR ("MutexOp") AML_EXT_OP, AML_EXT_MUTEX_OP, 2, 0, {EAmlName, EAmlUInt8, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE}, + /* 0x5B 0x02 */ {AML_DEBUG_STR ("EventOp") AML_EXT_OP, AML_EXT_EVENT_OP, 1, 0, {EAmlName, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE}, + /* 0x5B 0x12 */ {AML_DEBUG_STR ("CondRefOfOp") AML_EXT_OP, AML_EXT_COND_REF_OF_OP, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x13 */ {AML_DEBUG_STR ("CreateFieldOp") AML_EXT_OP, AML_EXT_CREATE_FIELD_OP,4, 3, {EAmlObject, EAmlObject, EAmlObject, EAmlName, EAmlNone, EAmlNone}, AML_IN_NAMESPACE}, + /* 0x5B 0x1F */ {AML_DEBUG_STR ("LoadTableOp") AML_EXT_OP, AML_EXT_LOAD_TABLE_OP, 6, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlObject, EAmlObject, EAmlObject}, 0}, + /* 0x5B 0x20 */ {AML_DEBUG_STR ("LoadOp") AML_EXT_OP, AML_EXT_LOAD_OP, 2, 0, {EAmlName, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x21 */ {AML_DEBUG_STR ("StallOp") AML_EXT_OP, AML_EXT_STALL_OP, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x22 */ {AML_DEBUG_STR ("SleepOp") AML_EXT_OP, AML_EXT_SLEEP_OP, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x23 */ {AML_DEBUG_STR ("AcquireOp") AML_EXT_OP, AML_EXT_ACQUIRE_OP, 2, 0, {EAmlObject, EAmlUInt16, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x24 */ {AML_DEBUG_STR ("SignalOp") AML_EXT_OP, AML_EXT_SIGNAL_OP, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x25 */ {AML_DEBUG_STR ("WaitOp") AML_EXT_OP, AML_EXT_WAIT_OP, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x26 */ {AML_DEBUG_STR ("ResetOp") AML_EXT_OP, AML_EXT_RESET_OP, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x27 */ {AML_DEBUG_STR ("ReleaseOp") AML_EXT_OP, AML_EXT_RELEASE_OP, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x28 */ {AML_DEBUG_STR ("FromBCDOp") AML_EXT_OP, AML_EXT_FROM_BCD_OP, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x29 */ {AML_DEBUG_STR ("ToBCDOp") AML_EXT_OP, AML_EXT_TO_BCD_OP, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x2A */ {AML_DEBUG_STR ("UnloadOp") AML_EXT_OP, AML_EXT_UNLOAD_OP, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x30 */ {AML_DEBUG_STR ("RevisionOp") AML_EXT_OP, AML_EXT_REVISION_OP, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x31 */ {AML_DEBUG_STR ("DebugOp") AML_EXT_OP, AML_EXT_DEBUG_OP, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x32 */ {AML_DEBUG_STR ("FatalOp") AML_EXT_OP, AML_EXT_FATAL_OP, 3, 0, {EAmlUInt8, EAmlUInt32, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x33 */ {AML_DEBUG_STR ("TimerOp") AML_EXT_OP, AML_EXT_TIMER_OP, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x5B 0x80 */ {AML_DEBUG_STR ("OpRegionOp") AML_EXT_OP, AML_EXT_REGION_OP, 4, 0, {EAmlName, EAmlUInt8, EAmlObject, EAmlObject, EAmlNone, EAmlNone}, AML_IN_NAMESPACE}, + /* 0x5B 0x81 */ {AML_DEBUG_STR ("FieldOp") AML_EXT_OP, AML_EXT_FIELD_OP, 2, 0, {EAmlName, EAmlUInt8, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_FIELD_LIST}, + /* 0x5B 0x82 */ {AML_DEBUG_STR ("DeviceOp") AML_EXT_OP, AML_EXT_DEVICE_OP, 1, 0, {EAmlName, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE}, + /* 0x5B 0x83 */ {AML_DEBUG_STR ("ProcessorOp") AML_EXT_OP, AML_EXT_PROCESSOR_OP, 4, 0, {EAmlName, EAmlUInt8, EAmlUInt32, EAmlUInt8, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE}, + /* 0x5B 0x84 */ {AML_DEBUG_STR ("PowerResOp") AML_EXT_OP, AML_EXT_POWER_RES_OP, 3, 0, {EAmlName, EAmlUInt8, EAmlUInt16, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE}, + /* 0x5B 0x85 */ {AML_DEBUG_STR ("ThermalZoneOp") AML_EXT_OP, AML_EXT_THERMAL_ZONE_OP,1, 0, {EAmlName, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE}, + /* 0x5B 0x86 */ {AML_DEBUG_STR ("IndexFieldOp") AML_EXT_OP, AML_EXT_INDEX_FIELD_OP, 3, 0, {EAmlName, EAmlName, EAmlUInt8, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_FIELD_LIST}, + /* 0x5B 0x87 */ {AML_DEBUG_STR ("BankFieldOp") AML_EXT_OP, AML_EXT_BANK_FIELD_OP, 4, 0, {EAmlName, EAmlName, EAmlObject, EAmlUInt8, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_FIELD_LIST}, + /* 0x5B 0x88 */ {AML_DEBUG_STR ("DataRegionOp") AML_EXT_OP, AML_EXT_DATA_REGION_OP, 4, 0, {EAmlName, EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone}, AML_IN_NAMESPACE}, + /* 0x5C */ {AML_DEBUG_STR ("RootChar") AML_ROOT_CHAR, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x5E */ {AML_DEBUG_STR ("ParentPrefixChar") AML_PARENT_PREFIX_CHAR, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x5F */ {AML_DEBUG_STR ("NameChar") '_', 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_NAME_CHAR}, + /* 0x60 */ {AML_DEBUG_STR ("Local0Op") AML_LOCAL0, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x61 */ {AML_DEBUG_STR ("Local1Op") AML_LOCAL1, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x62 */ {AML_DEBUG_STR ("Local2Op") AML_LOCAL2, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x63 */ {AML_DEBUG_STR ("Local3Op") AML_LOCAL3, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x64 */ {AML_DEBUG_STR ("Local4Op") AML_LOCAL4, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x65 */ {AML_DEBUG_STR ("Local5Op") AML_LOCAL5, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x66 */ {AML_DEBUG_STR ("Local6Op") AML_LOCAL6, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x67 */ {AML_DEBUG_STR ("Local7Op") AML_LOCAL7, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x68 */ {AML_DEBUG_STR ("Arg0Op") AML_ARG0, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x69 */ {AML_DEBUG_STR ("Arg1Op") AML_ARG1, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x6A */ {AML_DEBUG_STR ("Arg2Op") AML_ARG2, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x6B */ {AML_DEBUG_STR ("Arg3Op") AML_ARG3, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x6C */ {AML_DEBUG_STR ("Arg4Op") AML_ARG4, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x6D */ {AML_DEBUG_STR ("Arg5Op") AML_ARG5, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x6E */ {AML_DEBUG_STR ("Arg6Op") AML_ARG6, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x70 */ {AML_DEBUG_STR ("StoreOp") AML_STORE_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x71 */ {AML_DEBUG_STR ("RefOfOp") AML_REF_OF_OP, 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x72 */ {AML_DEBUG_STR ("AddOp") AML_ADD_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x73 */ {AML_DEBUG_STR ("ConcatOp") AML_CONCAT_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x74 */ {AML_DEBUG_STR ("SubtractOp") AML_SUBTRACT_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x75 */ {AML_DEBUG_STR ("IncrementOp") AML_INCREMENT_OP, 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x76 */ {AML_DEBUG_STR ("DecrementOp") AML_DECREMENT_OP, 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x77 */ {AML_DEBUG_STR ("MultiplyOp") AML_MULTIPLY_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x78 */ {AML_DEBUG_STR ("DivideOp") AML_DIVIDE_OP, 0, 4, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone}, 0}, + /* 0x79 */ {AML_DEBUG_STR ("ShiftLeftOp") AML_SHIFT_LEFT_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x7A */ {AML_DEBUG_STR ("ShiftRightOp") AML_SHIFT_RIGHT_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x7B */ {AML_DEBUG_STR ("AndOp") AML_AND_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x7C */ {AML_DEBUG_STR ("NAndOp") AML_NAND_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x7D */ {AML_DEBUG_STR ("OrOp") AML_OR_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x7E */ {AML_DEBUG_STR ("NorOp") AML_NOR_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x7F */ {AML_DEBUG_STR ("XOrOp") AML_XOR_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x80 */ {AML_DEBUG_STR ("NotOp") AML_NOT_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x81 */ {AML_DEBUG_STR ("FindSetLeftBitOp") AML_FIND_SET_LEFT_BIT_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x82 */ {AML_DEBUG_STR ("FindSetRightBitOp") AML_FIND_SET_RIGHT_BIT_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x83 */ {AML_DEBUG_STR ("DerefOfOp") AML_DEREF_OF_OP, 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x84 */ {AML_DEBUG_STR ("ConcatResOp") AML_CONCAT_RES_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x85 */ {AML_DEBUG_STR ("ModOp") AML_MOD_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x86 */ {AML_DEBUG_STR ("NotifyOp") AML_NOTIFY_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x87 */ {AML_DEBUG_STR ("SizeOfOp") AML_SIZE_OF_OP, 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x88 */ {AML_DEBUG_STR ("IndexOp") AML_INDEX_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x89 */ {AML_DEBUG_STR ("MatchOp") AML_MATCH_OP, 0, 6, 0, {EAmlObject, EAmlUInt8, EAmlObject, EAmlUInt8, EAmlObject, EAmlObject}, 0}, + /* 0x8A */ {AML_DEBUG_STR ("CreateDWordFieldOp") AML_CREATE_DWORD_FIELD_OP, 0, 3, 2, {EAmlObject, EAmlObject, EAmlName, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE}, + /* 0x8B */ {AML_DEBUG_STR ("CreateWordFieldOp") AML_CREATE_WORD_FIELD_OP, 0, 3, 2, {EAmlObject, EAmlObject, EAmlName, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE}, + /* 0x8C */ {AML_DEBUG_STR ("CreateByteFieldOp") AML_CREATE_BYTE_FIELD_OP, 0, 3, 2, {EAmlObject, EAmlObject, EAmlName, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE}, + /* 0x8D */ {AML_DEBUG_STR ("CreateBitFieldOp") AML_CREATE_BIT_FIELD_OP, 0, 3, 2, {EAmlObject, EAmlObject, EAmlName, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE}, + /* 0x8E */ {AML_DEBUG_STR ("ObjectTypeOp") AML_OBJECT_TYPE_OP, 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x8F */ {AML_DEBUG_STR ("CreateQWordFieldOp") AML_CREATE_QWORD_FIELD_OP, 0, 3, 2, {EAmlObject, EAmlObject, EAmlName, EAmlNone, EAmlNone, EAmlNone}, AML_IN_NAMESPACE}, + /* 0x90 */ {AML_DEBUG_STR ("LAndOp") AML_LAND_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x91 */ {AML_DEBUG_STR ("LOrOp") AML_LOR_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x92 */ {AML_DEBUG_STR ("LNotOp") AML_LNOT_OP, 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x93 */ {AML_DEBUG_STR ("LEqualOp") AML_LEQUAL_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x94 */ {AML_DEBUG_STR ("LGreaterOp") AML_LGREATER_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x95 */ {AML_DEBUG_STR ("LLessOp") AML_LLESS_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x96 */ {AML_DEBUG_STR ("ToBufferOp") AML_TO_BUFFER_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x97 */ {AML_DEBUG_STR ("ToDecimalStringOp") AML_TO_DEC_STRING_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x98 */ {AML_DEBUG_STR ("ToHexStringOp") AML_TO_HEX_STRING_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x99 */ {AML_DEBUG_STR ("ToIntegerOp") AML_TO_INTEGER_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x9C */ {AML_DEBUG_STR ("ToStringOp") AML_TO_STRING_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x9D */ {AML_DEBUG_STR ("CopyObjectOp") AML_COPY_OBJECT_OP, 0, 2, 0, {EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x9E */ {AML_DEBUG_STR ("MidOp") AML_MID_OP, 0, 3, 0, {EAmlObject, EAmlObject, EAmlObject, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0x9F */ {AML_DEBUG_STR ("ContinueOp") AML_CONTINUE_OP, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0xA0 */ {AML_DEBUG_STR ("IfOp") AML_IF_OP, 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ}, + /* 0xA1 */ {AML_DEBUG_STR ("ElseOp") AML_ELSE_OP, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ}, + /* 0xA2 */ {AML_DEBUG_STR ("WhileOp") AML_WHILE_OP, 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ}, + /* 0xA3 */ {AML_DEBUG_STR ("NoopOp") AML_NOOP_OP, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0xA4 */ {AML_DEBUG_STR ("ReturnOp") AML_RETURN_OP, 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0xA5 */ {AML_DEBUG_STR ("BreakOp") AML_BREAK_OP, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0xCC */ {AML_DEBUG_STR ("BreakPointOp") AML_BREAK_POINT_OP, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, + /* 0xD0 */ {AML_DEBUG_STR ("MethodInvocOp") AML_METHOD_INVOC_OP, 0, 2, 0, {EAmlName, EAmlUInt8, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_PSEUDO_OPCODE | AML_HAS_CHILD_OBJ}, + /* 0xFF */ {AML_DEBUG_STR ("OnesOp") AML_ONES_OP, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, 0}, +}; + +/** AML grammar encoding for field elements. + + Some AML objects are expecting a FieldList. They are referred in this library + as field nodes. These objects have the following opcodes: + - FieldOp; + - IndexFieldOp; + - BankFieldOp. + In the AML grammar encoding table, they have the AML_HAS_FIELD_LIST + attribute. + + A field list is made of field elements. + According to the ACPI 6.3 specification, s20.2.5.2 "Named Objects Encoding", + field elements can be: + - NamedField := NameSeg PkgLength; + - ReservedField := 0x00 PkgLength; + - AccessField := 0x01 AccessType AccessAttrib; + - ConnectField := <0x02 NameString> | <0x02 BufferData>; + - ExtendedAccessField := 0x03 AccessType ExtendedAccessAttrib AccessLength. + + A small set of opcodes describes field elements. They are referred in this + library as field opcodes. + The NamedField field element doesn't have a field opcode. A pseudo + OpCode/SubOpCode couple has been created for it. + + Field elements: + - don't have a SubOpCode; + - have at most 3 fixed arguments (6 for object opcodes, + 8 for method invocations); + - don't have variable list of arguments; + - are not part of the AML namespace, except NamedField field elements. +*/ +GLOBAL_REMOVE_IF_UNREFERENCED +STATIC +CONST +AML_BYTE_ENCODING mAmlFieldEncoding[] = { + // Comment Str OpCode SubOpCode MaxIndex NameIndex 0 1 2 3 4 5 Attribute + /* 0x00 */ {AML_DEBUG_STR ("FieldReservedOp") AML_FIELD_RESERVED_OP, 0, 0, 0, {EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_FIELD_ELEMENT | AML_HAS_PKG_LENGTH}, + /* 0x01 */ {AML_DEBUG_STR ("FieldAccessOp") AML_FIELD_ACCESS_OP, 0, 2, 0, {EAmlUInt8, EAmlUInt8, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_FIELD_ELEMENT}, + /* 0x02 */ {AML_DEBUG_STR ("FieldConnectionOp") AML_FIELD_CONNECTION_OP, 0, 1, 0, {EAmlObject, EAmlNone, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_FIELD_ELEMENT}, + /* 0x03 */ {AML_DEBUG_STR ("FieldExtAccessOp") AML_FIELD_EXT_ACCESS_OP, 0, 3, 0, {EAmlUInt8, EAmlUInt8, EAmlUInt8, EAmlNone, EAmlNone, EAmlNone}, AML_IS_FIELD_ELEMENT}, + /* 0x04 */ {AML_DEBUG_STR ("FieldNamed") AML_FIELD_NAMED_OP, 0, 2, 0, {EAmlName, EAmlFieldPkgLen, EAmlNone, EAmlNone, EAmlNone, EAmlNone}, AML_IS_FIELD_ELEMENT | AML_IS_PSEUDO_OPCODE | AML_IN_NAMESPACE} +}; + +/** Get the AML_BYTE_ENCODING entry in the AML encoding table. + + Note: For Pseudo OpCodes this function returns NULL. + + @param [in] Buffer Pointer to an OpCode/SubOpCode couple. + If *Buffer = 0x5b (extended OpCode), + Buffer must be at least two bytes long. + + @return The corresponding AML_BYTE_ENCODING entry. + NULL if not found. +**/ +CONST +AML_BYTE_ENCODING * +EFIAPI +AmlGetByteEncoding ( + IN CONST UINT8 * Buffer + ) +{ + UINT8 OpCode; + UINT8 SubOpCode; + UINT32 Index; + + if (Buffer == NULL) { + ASSERT (0); + return NULL; + } + + // Get OpCode and SubOpCode. + OpCode = Buffer[0]; + if (OpCode == AML_EXT_OP) { + SubOpCode = Buffer[1]; + } else { + SubOpCode = 0; + } + + // Search the table. + for (Index = 0; + Index < (sizeof (mAmlByteEncoding) / sizeof (mAmlByteEncoding[0])); + Index++) { + if ((mAmlByteEncoding[Index].OpCode == OpCode) && + (mAmlByteEncoding[Index].SubOpCode == SubOpCode)) { + if ((mAmlByteEncoding[Index].Attribute & AML_IS_PSEUDO_OPCODE) == + AML_IS_PSEUDO_OPCODE) { + // A pseudo OpCode cannot be parsed as it is internal to this library. + // The MethodInvocation encoding can be detected by NameSpace lookup. + ASSERT (0); + return NULL; + } + return &mAmlByteEncoding[Index]; + } + } + + return NULL; +} + +/** Get the AML_BYTE_ENCODING entry in the AML encoding table + by providing an OpCode/SubOpCode couple. + + @param [in] OpCode OpCode. + @param [in] SubOpCode SubOpCode. + + @return The corresponding AML_BYTE_ENCODING entry. + NULL if not found. +**/ +CONST +AML_BYTE_ENCODING * +EFIAPI +AmlGetByteEncodingByOpCode ( + IN UINT8 OpCode, + IN UINT8 SubOpCode + ) +{ + UINT32 Index; + + // Search the table. + for (Index = 0; + Index < (sizeof (mAmlByteEncoding) / sizeof (mAmlByteEncoding[0])); + Index++) { + if ((mAmlByteEncoding[Index].OpCode == OpCode) && + (mAmlByteEncoding[Index].SubOpCode == SubOpCode)) { + return &mAmlByteEncoding[Index]; + } + } + return NULL; +} + +/** Get the AML_BYTE_ENCODING entry in the field encoding table. + + Note: For Pseudo OpCodes this function returns NULL. + + @param [in] Buffer Pointer to a field OpCode. + No SubOpCode is expected. + + @return The corresponding AML_BYTE_ENCODING entry + in the field encoding table. + NULL if not found. +**/ +CONST +AML_BYTE_ENCODING * +EFIAPI +AmlGetFieldEncoding ( + IN CONST UINT8 * Buffer + ) +{ + UINT8 OpCode; + UINT32 Index; + + if (Buffer == NULL) { + ASSERT (0); + return NULL; + } + + // Get OpCode. + OpCode = *Buffer; + + // Search in the table. + for (Index = 0; + Index < (sizeof (mAmlFieldEncoding) / sizeof (mAmlFieldEncoding[0])); + Index++) { + if (mAmlFieldEncoding[Index].OpCode == OpCode) { + if ((mAmlFieldEncoding[Index].Attribute & AML_IS_PSEUDO_OPCODE) == + AML_IS_PSEUDO_OPCODE) { + // A pseudo OpCode cannot be parsed as it is internal to this library. + // The NamedField encoding can be detected because it begins with a + // char. + ASSERT (0); + return NULL; + } + return &mAmlFieldEncoding[Index]; + } + } + + return NULL; +} + +/** Get the AML_BYTE_ENCODING entry in the field encoding table + by providing an OpCode/SubOpCode couple. + + @param [in] OpCode OpCode. + @param [in] SubOpCode SubOpCode. + + @return The corresponding AML_BYTE_ENCODING entry + in the field encoding table. + NULL if not found. +**/ +CONST +AML_BYTE_ENCODING * +EFIAPI +AmlGetFieldEncodingByOpCode ( + IN UINT8 OpCode, + IN UINT8 SubOpCode + ) +{ + UINT32 Index; + + // Search the table. + for (Index = 0; + Index < (sizeof (mAmlFieldEncoding) / sizeof (mAmlFieldEncoding[0])); + Index++) { + if ((mAmlFieldEncoding[Index].OpCode == OpCode) && + (mAmlFieldEncoding[Index].SubOpCode == SubOpCode)) { + return &mAmlFieldEncoding[Index]; + } + } + return NULL; +} + +// Enable this function for debug. +#if !defined (MDEPKG_NDEBUG) +/** Look for an OpCode/SubOpCode couple in the AML grammar, + and return a corresponding string. + + @param [in] OpCode The OpCode. + @param [in] SubOpCode The SubOpCode. + + @return A string describing the OpCode/SubOpCode couple. + NULL if not found. +**/ +CONST +CHAR8 * +AmlGetOpCodeStr ( + IN UINT8 OpCode, + IN UINT8 SubOpCode + ) +{ + EAML_PARSE_INDEX Index; + + // Search the table. + for (Index = 0; + Index < (sizeof (mAmlByteEncoding) / sizeof (mAmlByteEncoding[0])); + Index++) { + if ((mAmlByteEncoding[Index].OpCode == OpCode) && + (mAmlByteEncoding[Index].SubOpCode == SubOpCode)) { + return mAmlByteEncoding[Index].Str; + } + } + + ASSERT (0); + return NULL; +} + +/** Look for an OpCode/SubOpCode couple in the AML field element grammar, + and return a corresponding string. + + @param [in] OpCode The OpCode. + @param [in] SubOpCode The SubOpCode. Must be zero. + + @return A string describing the OpCode/SubOpCode couple. + NULL if not found. +**/ +CONST +CHAR8 * +AmlGetFieldOpCodeStr ( + IN UINT8 OpCode, + IN UINT8 SubOpCode + ) +{ + EAML_PARSE_INDEX Index; + + if (SubOpCode != 0) { + ASSERT (0); + return NULL; + } + + // Search the table. + for (Index = 0; + Index < (sizeof (mAmlFieldEncoding) / sizeof (mAmlFieldEncoding[0])); + Index++) { + if ((mAmlFieldEncoding[Index].OpCode == OpCode)) { + return mAmlFieldEncoding[Index].Str; + } + } + + ASSERT (0); + return NULL; +} +#endif // MDEPKG_NDEBUG + +/** Check whether the OpCode/SubOpcode couple is a valid entry + in the AML grammar encoding table. + + @param [in] OpCode OpCode to check. + @param [in] SubOpCode SubOpCode to check. + + @retval TRUE The OpCode/SubOpCode couple is valid. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlIsOpCodeValid ( + IN UINT8 OpCode, + IN UINT8 SubOpCode + ) +{ + EAML_PARSE_INDEX Index; + + // Search the table. + for (Index = 0; + Index < (sizeof (mAmlByteEncoding) / sizeof (mAmlByteEncoding[0])); + Index++) { + if ((mAmlByteEncoding[Index].OpCode == OpCode) && + (mAmlByteEncoding[Index].SubOpCode == SubOpCode)) { + return TRUE; + } + } + return FALSE; +} + +/** AML_PARSE_FORMAT to EAML_NODE_DATA_TYPE translation table. + + AML_PARSE_FORMAT describes an internal set of values identifying the types + that can be found while parsing an AML bytestream. + EAML_NODE_DATA_TYPE describes an external set of values allowing to identify + what type of data can be found in data nodes. +*/ +GLOBAL_REMOVE_IF_UNREFERENCED +STATIC +CONST +EAML_NODE_DATA_TYPE mAmlTypeToNodeDataType[] = { + EAmlNodeDataTypeNone, // EAmlNone + EAmlNodeDataTypeUInt, // EAmlUInt8 + EAmlNodeDataTypeUInt, // EAmlUInt16 + EAmlNodeDataTypeUInt, // EAmlUInt32 + EAmlNodeDataTypeUInt, // EAmlUInt64 + EAmlNodeDataTypeReserved5, // EAmlObject + EAmlNodeDataTypeNameString, // EAmlName + EAmlNodeDataTypeString, // EAmlString + EAmlNodeDataTypeFieldPkgLen // EAmlFieldPkgLen +}; + +/** Convert an AML_PARSE_FORMAT to its corresponding EAML_NODE_DATA_TYPE. + + @param [in] AmlType Input AML Type. + + @return The corresponding EAML_NODE_DATA_TYPE. + EAmlNodeDataTypeNone if not found. +**/ +EAML_NODE_DATA_TYPE +EFIAPI +AmlTypeToNodeDataType ( + IN AML_PARSE_FORMAT AmlType + ) +{ + if (AmlType >= + (sizeof (mAmlTypeToNodeDataType) / sizeof (mAmlTypeToNodeDataType[0]))) { + ASSERT (0); + return EAmlNodeDataTypeNone; + } + + return mAmlTypeToNodeDataType[AmlType]; +} + +/** Get the package length from the buffer. + + @param [in] Buffer AML buffer. + @param [out] PkgLength The interpreted PkgLen value. + Length cannot exceed 2^28. + + @return The number of bytes to represent the package length. + 0 if an issue occurred. +**/ +UINT32 +EFIAPI +AmlGetPkgLength ( + IN CONST UINT8 * Buffer, + OUT UINT32 * PkgLength + ) +{ + UINT8 LeadByte; + UINT8 ByteCount; + UINT32 RealLength; + UINT32 Offset; + + if ((Buffer == NULL) || + (PkgLength == NULL)) { + ASSERT (0); + return 0; + } + + /* From ACPI 6.3 specification, s20.2.4 "Package Length Encoding": + + PkgLength := PkgLeadByte | + | + | + + + PkgLeadByte := + + + + Note: + The high 2 bits of the first byte reveal how many + follow bytes are in the PkgLength. If the + PkgLength has only one byte, bit 0 through 5 are + used to encode the package length (in other + words, values 0-63). If the package length value + is more than 63, more than one byte must be + used for the encoding in which case bit 4 and 5 of + the PkgLeadByte are reserved and must be zero. + If the multiple bytes encoding is used, bits 0-3 of + the PkgLeadByte become the least significant 4 + bits of the resulting package length value. The next + ByteData will become the next least + significant 8 bits of the resulting value and so on, + up to 3 ByteData bytes. Thus, the maximum + package length is 2**28. + */ + + LeadByte = *Buffer; + ByteCount = (LeadByte >> 6) & 0x03U; + Offset = ByteCount + 1U; + RealLength = 0; + + // Switch on the number of bytes used to store the PkgLen. + switch (ByteCount) { + case 0: + { + RealLength = LeadByte; + break; + } + case 1: + { + RealLength = *(Buffer + 1); + RealLength = (RealLength << 4) | (LeadByte & 0xF); + break; + } + case 2: + { + RealLength = *(Buffer + 1); + RealLength |= ((UINT32)(*(Buffer + 2))) << 8; + RealLength = (RealLength << 4) | (LeadByte & 0xF); + break; + } + case 3: + { + RealLength = *(Buffer + 1); + RealLength |= ((UINT32)(*(Buffer + 2))) << 8; + RealLength |= ((UINT32)(*(Buffer + 3))) << 16; + RealLength = (RealLength << 4) | (LeadByte & 0xF); + break; + } + default: + { + ASSERT (0); + Offset = 0; + break; + } + } // switch + + *PkgLength = RealLength; + + return Offset; +} + +/** Convert the Length to the AML PkgLen encoding, + then and write it in the Buffer. + + @param [in] Length Length to convert. + Length cannot exceed 2^28. + @param [out] Buffer Write the result in this Buffer. + + @return The number of bytes used to write the Length. +**/ +UINT8 +EFIAPI +AmlSetPkgLength ( + IN UINT32 Length, + OUT UINT8 * Buffer + ) +{ + UINT8 LeadByte; + UINT8 Offset; + UINT8 CurrentOffset; + UINT8 CurrentShift; + UINT32 ComputedLength; + + if (Buffer == NULL) { + ASSERT (0); + return 0; + } + + LeadByte = 0; + Offset = 0; + + if ((Length < (1 << 6))) { + // Length < 2^6, only need one byte to encode it. + LeadByte = (UINT8)Length; + + } else { + // Need more than one byte to encode it. + // Test Length to find how many bytes are needed. + + if (Length >= (1 << 28)) { + // Length >= 2^28, should not be possible. + ASSERT (0); + return 0; + + } else if (Length >= (1 << 20)) { + // Length >= 2^20 + Offset = 3; + + } else if (Length >= (1 << 12)) { + // Length >= 2^12 + Offset = 2; + + } else if (Length >= (1 << 6)) { + // Length >= 2^6 + Offset = 1; + + } else { + // Should not be possible. + ASSERT (0); + return 0; + } + + // Set the LeadByte. + LeadByte = (UINT8)(Offset << 6); + LeadByte = (UINT8)(LeadByte | (Length & 0xF)); + } + + // Write to the Buffer. + *Buffer = LeadByte; + CurrentOffset = 1; + while (CurrentOffset < (Offset + 1)) { + CurrentShift = (UINT8)((CurrentOffset - 1) * 8); + ComputedLength = Length & (UINT32)(0x00000FF0 << CurrentShift); + ComputedLength = (ComputedLength) >> (4 + CurrentShift); + LeadByte = (UINT8)(ComputedLength & 0xFF); + *(Buffer + CurrentOffset) = LeadByte; + CurrentOffset++; + } + + return ++Offset; +} + +/** Compute the number of bytes required to write a package length. + + @param [in] Length The length to convert in the AML package length + encoding style. + Length cannot exceed 2^28. + + @return The number of bytes required to write the Length. +**/ +UINT8 +EFIAPI +AmlComputePkgLengthWidth ( + IN UINT32 Length + ) +{ + // Length >= 2^28, should not be possible. + if (Length >= (1 << 28)) { + ASSERT (0); + return 0; + + } else if (Length >= (1 << 20)) { + // Length >= 2^20 + return 4; + + } else if (Length >= (1 << 12)) { + // Length >= 2^12 + return 3; + + } else if (Length >= (1 << 6)) { + // Length >= 2^6 + return 2; + } + + // Length < 2^6 + return 1; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.h new file mode 100644 index 000000000..ba1228621 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlEncoding/Aml.h @@ -0,0 +1,330 @@ +/** @file + AML grammar definitions. + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_H_ +#define AML_H_ + +#include +#include +#include + +#if !defined (MDEPKG_NDEBUG) +#define AML_DEBUG_STR(str) str, +#else +#define AML_DEBUG_STR(x) +#endif // MDEPKG_NDEBUG + +/** AML types. + + In the AML bytestream, data is represented using one of the following types. + These types are used in the parsing logic to know what kind of data is + expected next in the bytestream. This allows to parse data according + to the AML_PARSE_FORMAT type. + E.g.: A string will not be parsed in the same way as a UINT8. + + These are internal types. +*/ +typedef enum EAmlParseFormat { + EAmlNone = 0, ///< No data expected. + EAmlUInt8, ///< One byte value evaluated as a UINT8. + EAmlUInt16, ///< Two byte value evaluated as a UINT16. + EAmlUInt32, ///< Four byte value evaluated as a UINT32. + EAmlUInt64, ///< Eight byte value evaluated as a UINT64. + EAmlObject, ///< AML object, starting with an OpCode/SubOpCode + /// couple, potentially followed by package length. + /// EAmlName is a subtype of an EAmlObject. + /// Indeed, an EAmlName can also be evaluated as + /// an EAmlObject in the parsing. + EAmlName, ///< Name corresponding to the NameString keyword + /// in the ACPI specification. E.g.: "\_SB_.DEV0" + EAmlString, ///< NULL terminated string. + EAmlFieldPkgLen, ///< A field package length (PkgLen). A data node of this + /// type can only be found in a field list, in a + /// NamedField statement. The PkgLen is otherwise + /// part of the object node structure. + EAmlParseFormatMax ///< Max enum. +} AML_PARSE_FORMAT; + +/** AML attributes + + To add some more information to the byte encoding, it is possible to add + these attributes. +*/ +typedef UINT32 AML_OP_ATTRIBUTE; + +/** A PkgLength is expected between the OpCode/SubOpCode couple and the first + fixed argument of the object. +*/ +#define AML_HAS_PKG_LENGTH 0x00001U + +/** The object's OpCode is actually a character. Encodings with this attribute + don't describe objects. The dual/multi name prefix have this attribute, + indicating the start of a longer NameString. +*/ +#define AML_IS_NAME_CHAR 0x00002U + +/** A variable list of arguments is following the last fixed argument. Each + argument is evaluated as an EAmlObject. +*/ +#define AML_HAS_CHILD_OBJ 0x00004U + +/** This is a sub-type of a variable list of arguments. It can only be + found in buffer objects. A ByteList is either a list of + bytes or a list of resource data elements. Resource data elements + have specific opcodes. +*/ +#define AML_HAS_BYTE_LIST 0x00008U + +/** This is a sub-type of a variable list of arguments. It can only be + found in Fields, IndexFields and BankFields. + A FieldList is made of FieldElements. FieldElements have specific opcodes. +*/ +#define AML_HAS_FIELD_LIST 0x00010U + +/** This object node is a field element. Its opcode is to be fetched from + the field encoding table. +*/ +#define AML_IS_FIELD_ELEMENT 0x00020U + +/** The object has a name and which is part of the AML namespace. The name + can be found in the fixed argument list at the NameIndex. +*/ +#define AML_IN_NAMESPACE 0x10000U + +/** Some OpCodes have been created in this library. They are called + pseudo opcodes and must stay internal to this library. +*/ +#define AML_IS_PSEUDO_OPCODE 0x20000U + +/** Encoding of an AML object. + + Every AML object has a specific encoding. This encoding information + is used to parse AML objects. A table of AML_BYTE_ENCODING entries + allows to parse an AML bytestream. + This structure is also used to describe field objects. + + Cf. ACPI 6.3 specification, s20.2. +*/ +typedef struct _AML_BYTE_ENCODING { +// Enable this field for debug. +#if !defined (MDEPKG_NDEBUG) + /// String field allowing to print the AML object. + CONST CHAR8 * Str; +#endif // MDEPKG_NDEBUG + + /// OpCode of the AML object. + UINT8 OpCode; + + /// SubOpCode of the AML object. + /// The SubOpcode field has a valid value when the OpCode is 0x5B, + /// otherwise this field must be zero. + /// For field objects, the SubOpCode is not used. + UINT8 SubOpCode; + + /// Number of fixed arguments for the AML statement represented + /// by the OpCode & SubOpcode. + /// Maximum is 6 for AML objects. + /// Maximum is 3 for field objects. + EAML_PARSE_INDEX MaxIndex; + + /// If the encoding has the AML_IN_NAMESPACE attribute (cf Attribute + /// field below), indicate where to find the name in the fixed list + /// of arguments. + EAML_PARSE_INDEX NameIndex; + + /// Type of each fixed argument. + AML_PARSE_FORMAT Format[EAmlParseIndexMax]; + + /// Additional information on the AML object. + AML_OP_ATTRIBUTE Attribute; +} AML_BYTE_ENCODING; + +/** Get the AML_BYTE_ENCODING entry in the AML encoding table. + + Note: For Pseudo OpCodes this function returns NULL. + + @param [in] Buffer Pointer to an OpCode/SubOpCode couple. + If *Buffer = 0x5b (extended OpCode), + Buffer must be at least two bytes long. + + @return The corresponding AML_BYTE_ENCODING entry. + NULL if not found. +**/ +CONST +AML_BYTE_ENCODING * +EFIAPI +AmlGetByteEncoding ( + IN CONST UINT8 * Buffer + ); + +/** Get the AML_BYTE_ENCODING entry in the AML encoding table + by providing an OpCode/SubOpCode couple. + + @param [in] OpCode OpCode. + @param [in] SubOpCode SubOpCode. + + @return The corresponding AML_BYTE_ENCODING entry. + NULL if not found. +**/ +CONST +AML_BYTE_ENCODING * +EFIAPI +AmlGetByteEncodingByOpCode ( + IN UINT8 OpCode, + IN UINT8 SubOpCode + ); + +/** Get the AML_BYTE_ENCODING entry in the field encoding table. + + Note: For Pseudo OpCodes this function returns NULL. + + @param [in] Buffer Pointer to a field OpCode. + No SubOpCode is expected. + + @return The corresponding AML_BYTE_ENCODING entry + in the field encoding table. + NULL if not found. +**/ +CONST +AML_BYTE_ENCODING * +EFIAPI +AmlGetFieldEncoding ( + IN CONST UINT8 * Buffer + ); + +/** Get the AML_BYTE_ENCODING entry in the field encoding table + by providing an OpCode/SubOpCode couple. + + @param [in] OpCode OpCode. + @param [in] SubOpCode SubOpCode. + + @return The corresponding AML_BYTE_ENCODING entry + in the field encoding table. + NULL if not found. +**/ +CONST +AML_BYTE_ENCODING * +EFIAPI +AmlGetFieldEncodingByOpCode ( + IN UINT8 OpCode, + IN UINT8 SubOpCode + ); + +// Enable this function for debug. +#if !defined (MDEPKG_NDEBUG) +/** Look for an OpCode/SubOpCode couple in the AML grammar, + and return a corresponding string. + + @param [in] OpCode The OpCode. + @param [in] SubOpCode The SubOpCode. + + @return A string describing the OpCode/SubOpCode couple. + NULL if not found. +**/ +CONST +CHAR8 * +AmlGetOpCodeStr ( + IN UINT8 OpCode, + IN UINT8 SubOpCode + ); + +/** Look for an OpCode/SubOpCode couple in the AML field element grammar, + and return a corresponding string. + + @param [in] OpCode The OpCode. + @param [in] SubOpCode The SubOpCode. Must be zero. + + @return A string describing the OpCode/SubOpCode couple. + NULL if not found. +**/ +CONST +CHAR8 * +AmlGetFieldOpCodeStr ( + IN UINT8 OpCode, + IN UINT8 SubOpCode + ); +#endif // MDEPKG_NDEBUG + +/** Check whether the OpCode/SubOpcode couple is a valid entry + in the AML grammar encoding table. + + @param [in] OpCode OpCode to check. + @param [in] SubOpCode SubOpCode to check. + + @retval TRUE The OpCode/SubOpCode couple is valid. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlIsOpCodeValid ( + IN UINT8 OpCode, + IN UINT8 SubOpCode + ); + +/** Convert an AML_PARSE_FORMAT to its corresponding EAML_NODE_DATA_TYPE. + + @param [in] AmlType Input AML Type. + + @return The corresponding EAML_NODE_DATA_TYPE. + EAmlNodeDataTypeNone if not found. +**/ +EAML_NODE_DATA_TYPE +EFIAPI +AmlTypeToNodeDataType ( + IN AML_PARSE_FORMAT AmlType + ); + +/** Get the package length from the buffer. + + @param [in] Buffer AML buffer. + @param [out] PkgLength The interpreted PkgLen value. + Length cannot exceed 2^28. + + @return The number of bytes to represent the package length. + 0 if an issue occurred. +**/ +UINT32 +EFIAPI +AmlGetPkgLength ( + IN CONST UINT8 * Buffer, + OUT UINT32 * PkgLength + ); + +/** Convert the Length to the AML PkgLen encoding, + then and write it in the Buffer. + + @param [in] Length Length to convert. + Length cannot exceed 2^28. + @param [out] Buffer Write the result in this Buffer. + + @return The number of bytes used to write the Length. +**/ +UINT8 +EFIAPI +AmlSetPkgLength ( + IN UINT32 Length, + OUT UINT8 * Buffer + ); + +/** Compute the number of bytes required to write a package length. + + @param [in] Length The length to convert in the AML package length + encoding style. + Length cannot exceed 2^28. + + @return The number of bytes required to write the Length. +**/ +UINT8 +EFIAPI +AmlComputePkgLengthWidth ( + IN UINT32 Length + ); + +#endif // AML_H_ + diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlInclude.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlInclude.h new file mode 100644 index 000000000..274482f0d --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlInclude.h @@ -0,0 +1,18 @@ +/** @file + AML Include file + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_INCLUDE_H_ +#define AML_INCLUDE_H_ + +#include +#include +#include +#include +#include + +#endif // AML_INCLUDE_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf new file mode 100644 index 000000000..e2babef44 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf @@ -0,0 +1,76 @@ +## @file +# AML Generation Library +# +# Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = DynamicAmlLib + FILE_GUID = 23A6AFDA-F2A5-45EC-BEFF-420639D345B9 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = AmlLib + +[Sources] + AmlCoreInterface.h + AmlDefines.h + AmlInclude.h + AmlNodeDefines.h + AmlDbgPrint/AmlDbgPrint.c + AmlDbgPrint/AmlDbgPrint.h + AmlEncoding/Aml.c + AmlEncoding/Aml.h + Api/AmlApi.c + Api/AmlApiHelper.c + Api/AmlApiHelper.h + Api/AmlResourceDataApi.c + CodeGen/AmlCodeGen.c + CodeGen/AmlResourceDataCodeGen.c + CodeGen/AmlResourceDataCodeGen.h + NameSpace/AmlNameSpace.c + NameSpace/AmlNameSpace.h + Parser/AmlFieldListParser.c + Parser/AmlFieldListParser.h + Parser/AmlMethodParser.c + Parser/AmlMethodParser.h + Parser/AmlParser.c + Parser/AmlParser.h + Parser/AmlResourceDataParser.c + Parser/AmlResourceDataParser.h + ResourceData/AmlResourceData.c + ResourceData/AmlResourceData.h + Serialize/AmlSerialize.c + Stream/AmlStream.c + Stream/AmlStream.h + String/AmlString.c + String/AmlString.h + Tree/AmlClone.c + Tree/AmlTreeIterator.h + Tree/AmlNode.c + Tree/AmlNode.h + Tree/AmlNodeInterface.c + Tree/AmlTree.c + Tree/AmlTree.h + Tree/AmlTreeEnumerator.c + Tree/AmlTreeIterator.c + Tree/AmlTreeTraversal.c + Tree/AmlTreeTraversal.h + Utils/AmlUtility.c + Utils/AmlUtility.h + +[Packages] + MdePkg/MdePkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + +[LibraryClasses] + BaseLib + +[BuildOptions] + *_*_*_CC_FLAGS = -DAML_HANDLE + +[Protocols] + +[Guids] diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h new file mode 100644 index 000000000..fffba6d54 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h @@ -0,0 +1,183 @@ +/** @file + AML Node Definition. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_NODE_DEFINES_H_ +#define AML_NODE_DEFINES_H_ + +#include +#include + +/** AML header node. + + This abstract class represents either a root/object/data node. + All the enumerated nodes have this same common header. +*/ +typedef struct AmlNodeHeader { + /// This must be the first field in this structure. + LIST_ENTRY Link; + + /// Parent of this node. NULL for the root node. + struct AmlNodeHeader * Parent; + + /// Node type allowing to identify a root/object/data node. + EAML_NODE_TYPE NodeType; +} AML_NODE_HEADER; + +/** Node handle. +*/ +typedef AML_NODE_HEADER* AML_NODE_HANDLE; + +/** AML root node. + + The root node is unique and at the head of of tree. It is a fake node used + to maintain the list of AML statements (stored as object nodes) which are + at the first scope level. +*/ +typedef struct AmlRootNode { + /// Header information. Must be the first field of the struct. + AML_NODE_HEADER NodeHeader; + + /// List of object nodes being at the first scope level. + /// These are children and can only be object nodes. + LIST_ENTRY VariableArgs; + + /// ACPI DSDT/SSDT header. + EFI_ACPI_DESCRIPTION_HEADER * SdtHeader; +} AML_ROOT_NODE; + +/** Root Node handle. +*/ +typedef AML_ROOT_NODE* AML_ROOT_NODE_HANDLE; + +/** AML object node. + + Object nodes match AML statements. They are associated with an + OpCode/SubOpCode, and can have children. +*/ +typedef struct AmlObjectNode { + /// Header information. Must be the first field of the struct. + AML_NODE_HEADER NodeHeader; + + /// Some object nodes have a variable list of arguments. + /// These are children and can only be object/data nodes. + /// Cf ACPI specification, s20.3. + LIST_ENTRY VariableArgs; + + /// Fixed arguments of this object node. + /// These are children and can be object/data nodes. + /// Cf ACPI specification, s20.3. + AML_NODE_HEADER * FixedArgs[EAmlParseIndexMax]; + + /// AML byte encoding. Stores the encoding information: + /// (OpCode/SubOpCode/number of fixed arguments/ attributes). + CONST AML_BYTE_ENCODING * AmlByteEncoding; + + /// Some nodes have a PkgLen following their OpCode/SubOpCode in the + /// AML bytestream. This field stores the decoded value of the PkgLen. + UINT32 PkgLen; +} AML_OBJECT_NODE; + +/** Object Node handle. +*/ +typedef AML_OBJECT_NODE* AML_OBJECT_NODE_HANDLE; + +/** AML data node. + + Data nodes store the smallest pieces of information. + E.g.: UINT8, UINT64, NULL terminated string, etc. + Data node don't have children nodes. +*/ +typedef struct AmlDataNode { + /// Header information. Must be the first field of the struct. + AML_NODE_HEADER NodeHeader; + + /// Tag identifying what data is stored in this node. + /// E.g. UINT, NULL terminated string, resource data element, etc. + EAML_NODE_DATA_TYPE DataType; + + /// Buffer containing the data stored by this node. + UINT8 * Buffer; + + /// Size of the Buffer. + UINT32 Size; +} AML_DATA_NODE; + +/** Data Node handle. +*/ +typedef AML_DATA_NODE* AML_DATA_NODE_HANDLE; + +/** Check whether a Node has a valid NodeType. + + @param [in] Node The node to check. + + @retval TRUE The Node has a valid NodeType. + @retval FALSE Otherwise. +*/ +#define IS_AML_NODE_VALID(Node) \ + ((Node != NULL) && \ + ((((CONST AML_NODE_HEADER*)Node)->NodeType > EAmlNodeUnknown) || \ + (((CONST AML_NODE_HEADER*)Node)->NodeType < EAmlNodeMax))) + +/** Check whether a Node is a root node. + + @param [in] Node The node to check. + + @retval TRUE The Node is a root node. + @retval FALSE Otherwise. +*/ +#define IS_AML_ROOT_NODE(Node) \ + ((Node != NULL) && \ + (((CONST AML_NODE_HEADER*)Node)->NodeType == EAmlNodeRoot)) + +/** Check whether a Node is an object node. + + @param [in] Node The node to check. + + @retval TRUE The Node is an object node. + @retval FALSE Otherwise. +*/ +#define IS_AML_OBJECT_NODE(Node) \ + ((Node != NULL) && \ + (((CONST AML_NODE_HEADER*)Node)->NodeType == EAmlNodeObject)) + +/** Check whether a Node is a data node. + + @param [in] Node The node to check. + + @retval TRUE The Node is a data node. + @retval FALSE Otherwise. +*/ +#define IS_AML_DATA_NODE(Node) \ + ((Node != NULL) && \ + (((CONST AML_NODE_HEADER*)Node)->NodeType == EAmlNodeData)) + +/** Check whether a Node has a parent. + + @param [in] Node The node to check. + + @retval TRUE The Node is a data node. + @retval FALSE Otherwise. +*/ +#define AML_NODE_HAS_PARENT(Node) \ + (IS_AML_NODE_VALID (Node) && \ + (((CONST AML_NODE_HEADER*)Node)->Parent != NULL)) + +/** Check that the Node is not attached somewhere. + This doesn't mean the node cannot have children. + + @param [in] Node The node to check. + + @retval TRUE The Node has been detached. + @retval FALSE Otherwise. +*/ +#define AML_NODE_IS_DETACHED(Node) \ + (IS_AML_NODE_VALID (Node) && \ + IsListEmpty ((CONST LIST_ENTRY*)Node) && \ + (((CONST AML_NODE_HEADER*)Node)->Parent == NULL)) + +#endif // AML_NODE_DEFINES_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c new file mode 100644 index 000000000..fdf04acc6 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c @@ -0,0 +1,382 @@ +/** @file + AML Api. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +/* Even though this file has access to the internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. Only the external node + handle types should be used, i.e. AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, + etc. + Indeed, the functions in the "Api" folder should be implemented only + using the "safe" functions available in the "Include" folder. This + makes the functions available in the "Api" folder easy to export. +*/ +#include + +#include +#include +#include +#include + +/** Update the name of a DeviceOp object node. + + @param [in] DeviceOpNode Object node representing a Device. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + OpCode/SubOpCode. + DeviceOp object nodes are defined in ASL + using the "Device ()" function. + @param [in] NewNameString The new Device's name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + The input string is copied. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeviceOpUpdateName ( + IN AML_OBJECT_NODE_HANDLE DeviceOpNode, + IN CHAR8 * NewNameString + ) +{ + EFI_STATUS Status; + + AML_DATA_NODE_HANDLE DeviceNameDataNode; + CHAR8 * NewAmlNameString; + UINT32 NewAmlNameStringSize; + + // Check the input node is an object node. + if ((DeviceOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)DeviceOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (DeviceOpNode, AML_EXT_OP, AML_EXT_DEVICE_OP)) || + (NewNameString == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the Device's name, being a data node + // which is the 1st fixed argument (i.e. index 0). + DeviceNameDataNode = (AML_DATA_NODE_HANDLE)AmlGetFixedArgument ( + DeviceOpNode, + EAmlParseIndexTerm0 + ); + if ((DeviceNameDataNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)DeviceNameDataNode) != EAmlNodeData) || + (!AmlNodeHasDataType (DeviceNameDataNode, EAmlNodeDataTypeNameString))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = ConvertAslNameToAmlName (NewNameString, &NewAmlNameString); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlGetNameStringSize (NewAmlNameString, &NewAmlNameStringSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Update the Device's name node. + Status = AmlUpdateDataNode ( + DeviceNameDataNode, + EAmlNodeDataTypeNameString, + (UINT8*)NewAmlNameString, + NewAmlNameStringSize + ); + ASSERT_EFI_ERROR (Status); + +exit_handler: + FreePool (NewAmlNameString); + return Status; +} + +/** Update an integer value defined by a NameOp object node. + + For compatibility reasons, the NameOpNode must initially + contain an integer. + + @param [in] NameOpNode NameOp object node. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] NewInt New Integer value to assign. + Must be a UINT64. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpUpdateInteger ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN UINT64 NewInt + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE_HANDLE IntegerOpNode; + + if ((NameOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (NameOpNode, AML_NAME_OP, 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the Integer object node defined by the "Name ()" function: + // it must have an Integer OpCode (Byte/Word/DWord/QWord). + // It is the 2nd fixed argument (i.e. index 1) of the NameOp node. + // This can also be a ZeroOp or OneOp node. + IntegerOpNode = (AML_OBJECT_NODE_HANDLE)AmlGetFixedArgument ( + NameOpNode, + EAmlParseIndexTerm1 + ); + if ((IntegerOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)IntegerOpNode) != EAmlNodeObject)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update the Integer value. + Status = AmlUpdateInteger (IntegerOpNode, NewInt); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Update a string value defined by a NameOp object node. + + The NameOpNode must initially contain a string. + The EISAID ASL macro converts a string to an integer. This, it is + not accepted. + + @param [in] NameOpNode NameOp object node. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] NewName New NULL terminated string to assign to + the NameOpNode. + The input string is copied. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpUpdateString ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN CONST CHAR8 * NewName + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE_HANDLE StringOpNode; + AML_DATA_NODE_HANDLE StringDataNode; + + if ((NameOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (NameOpNode, AML_NAME_OP, 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the String object node defined by the "Name ()" function: + // it must have a string OpCode. + // It is the 2nd fixed argument (i.e. index 1) of the NameOp node. + StringOpNode = (AML_OBJECT_NODE_HANDLE)AmlGetFixedArgument ( + NameOpNode, + EAmlParseIndexTerm1 + ); + if ((StringOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)StringOpNode) != EAmlNodeObject)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the string data node. + // It is the 1st fixed argument (i.e. index 0) of the StringOpNode node. + StringDataNode = (AML_DATA_NODE_HANDLE)AmlGetFixedArgument ( + StringOpNode, + EAmlParseIndexTerm0 + ); + if ((StringDataNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)StringDataNode) != EAmlNodeData)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update the string value. + Status = AmlUpdateDataNode ( + StringDataNode, + EAmlNodeDataTypeString, + (UINT8*)NewName, + (UINT32)AsciiStrLen (NewName) + 1 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Get the first Resource Data element contained in a "_CRS" object. + + In the following ASL code, the function will return the Resource Data + node corresponding to the "QWordMemory ()" ASL macro. + Name (_CRS, ResourceTemplate() { + QWordMemory (...) {...}, + Interrupt (...) {...} + } + ) + + Note: + - The "_CRS" object must be declared using ASL "Name (Declare Named Object)". + - "_CRS" declared using ASL "Method (Declare Control Method)" is not + supported. + + @param [in] NameOpCrsNode NameOp object node defining a "_CRS" object. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [out] OutRdNode Pointer to the first Resource Data element of + the "_CRS" object. A Resource Data element + is stored in a data node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpCrsGetFirstRdNode ( + IN AML_OBJECT_NODE_HANDLE NameOpCrsNode, + OUT AML_DATA_NODE_HANDLE * OutRdNode + ) +{ + AML_OBJECT_NODE_HANDLE BufferOpNode; + AML_DATA_NODE_HANDLE FirstRdNode; + + if ((NameOpCrsNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameOpCrsNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (NameOpCrsNode, AML_NAME_OP, 0)) || + (!AmlNameOpCompareName (NameOpCrsNode, "_CRS")) || + (OutRdNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutRdNode = NULL; + + // Get the _CRS value which is represented as a BufferOp object node + // which is the 2nd fixed argument (i.e. index 1). + BufferOpNode = (AML_OBJECT_NODE_HANDLE)AmlGetFixedArgument ( + NameOpCrsNode, + EAmlParseIndexTerm1 + ); + if ((BufferOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)BufferOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (BufferOpNode, AML_BUFFER_OP, 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the first Resource data node in the variable list of + // argument of the BufferOp node. + FirstRdNode = (AML_DATA_NODE_HANDLE)AmlGetNextVariableArgument ( + (AML_NODE_HANDLE)BufferOpNode, + NULL + ); + if ((FirstRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)FirstRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType (FirstRdNode, EAmlNodeDataTypeResourceData))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutRdNode = FirstRdNode; + return EFI_SUCCESS; +} + +/** Get the Resource Data element following the CurrRdNode Resource Data. + + In the following ASL code, if CurrRdNode corresponds to the first + "QWordMemory ()" ASL macro, the function will return the Resource Data + node corresponding to the "Interrupt ()" ASL macro. + Name (_CRS, ResourceTemplate() { + QwordMemory (...) {...}, + Interrupt (...) {...} + } + ) + + The CurrRdNode Resource Data node must be defined in an object named "_CRS" + and defined by a "Name ()" ASL function. + + @param [in] CurrRdNode Pointer to the current Resource Data element of + the "_CRS" object. + @param [out] OutRdNode Pointer to the Resource Data element following + the CurrRdNode. + Contain a NULL pointer if CurrRdNode is the + last Resource Data element in the list. + The "End Tag" is not considered as a resource + data element and is not returned. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpCrsGetNextRdNode ( + IN AML_DATA_NODE_HANDLE CurrRdNode, + OUT AML_DATA_NODE_HANDLE * OutRdNode + ) +{ + AML_OBJECT_NODE_HANDLE NameOpCrsNode; + AML_OBJECT_NODE_HANDLE BufferOpNode; + + if ((CurrRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)CurrRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType (CurrRdNode, EAmlNodeDataTypeResourceData)) || + (OutRdNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutRdNode = NULL; + + // The parent of the CurrRdNode must be a BufferOp node. + BufferOpNode = (AML_OBJECT_NODE_HANDLE)AmlGetParent ( + (AML_NODE_HANDLE)CurrRdNode + ); + if ((BufferOpNode == NULL) || + (!AmlNodeHasOpCode (BufferOpNode, AML_BUFFER_OP, 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // The parent of the BufferOpNode must be a NameOp node. + NameOpCrsNode = (AML_OBJECT_NODE_HANDLE)AmlGetParent ( + (AML_NODE_HANDLE)BufferOpNode + ); + if ((NameOpCrsNode == NULL) || + (!AmlNodeHasOpCode (NameOpCrsNode, AML_NAME_OP, 0)) || + (!AmlNameOpCompareName (NameOpCrsNode, "_CRS"))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutRdNode = (AML_DATA_NODE_HANDLE)AmlGetNextVariableArgument ( + (AML_NODE_HANDLE)BufferOpNode, + (AML_NODE_HANDLE)CurrRdNode + ); + + // If the Resource Data is an End Tag, return NULL. + if (AmlNodeHasRdDataType ( + *OutRdNode, + AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) { + *OutRdNode = NULL; + } + + return EFI_SUCCESS; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c new file mode 100644 index 000000000..9693f28b5 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c @@ -0,0 +1,219 @@ +/** @file + AML Helper. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +/* Even though this file has access to the internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. Only the external node + handle types should be used, i.e. AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, + etc. + Indeed, the functions in the "Api" folder should be implemented only + using the "safe" functions available in the "Include" folder. This + makes the functions available in the "Api" folder easy to export. +*/ +#include + +#include +#include +#include + +/** Compare the NameString defined by the "Name ()" ASL function, + and stored in the NameOpNode, with the input NameString. + + An ASL NameString is expected to be NULL terminated, and can be composed + of NameSegs that have less that 4 chars, like "DEV". "DEV" will be expanded + as "DEV_". + + An AML NameString is not NULL terminated and is is only composed of + 4 chars long NameSegs. + + @param [in] NameOpNode NameOp object node defining a variable. + Must have an AML_NAME_OP/0 OpCode/SubOpCode. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] AslName ASL NameString to compare the NameOp's name with. + Must be NULL terminated. + + @retval TRUE If the AslName and the AmlName defined by the NameOp node + are similar. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNameOpCompareName ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN CHAR8 * AslName + ) +{ + EFI_STATUS Status; + AML_DATA_NODE_HANDLE NameDataNode; + + CHAR8 * AmlName; + UINT32 AmlNameSize; + + BOOLEAN RetVal; + + if ((NameOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (NameOpNode, AML_NAME_OP, 0)) || + (AslName == NULL)) { + ASSERT (0); + return FALSE; + } + + // Get the NameOp name, being in a data node + // which is the first fixed argument (i.e. index 0). + NameDataNode = (AML_DATA_NODE_HANDLE)AmlGetFixedArgument ( + NameOpNode, + EAmlParseIndexTerm0 + ); + if ((NameDataNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameDataNode) != EAmlNodeData) || + (!AmlNodeHasDataType (NameDataNode, EAmlNodeDataTypeNameString))) { + ASSERT (0); + return FALSE; + } + + // Get the size of the name. + Status = AmlGetDataNodeBuffer (NameDataNode, NULL, &AmlNameSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // Allocate memory to fetch the name. + AmlName = AllocateZeroPool (AmlNameSize); + if (AmlName == NULL) { + ASSERT (0); + return FALSE; + } + + // Fetch the name. + Status = AmlGetDataNodeBuffer (NameDataNode, (UINT8*)AmlName, &AmlNameSize); + if (EFI_ERROR (Status)) { + FreePool (AmlName); + ASSERT (0); + return FALSE; + } + + // Compare the input AslName and the AmlName stored in the NameOp node. + RetVal = CompareAmlWithAslNameString (AmlName, AslName); + + // Free the string buffer. + FreePool (AmlName); + return RetVal; +} + +/** Check whether ObjectNode has the input OpCode/SubOpcode couple. + + @param [in] ObjectNode Pointer to an object node. + @param [in] OpCode OpCode to check + @param [in] SubOpCode SubOpCode to check + + @retval TRUE The node is an object node and + the Opcode and SubOpCode match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasOpCode ( + IN AML_OBJECT_NODE_HANDLE ObjectNode, + IN UINT8 OpCode, + IN UINT8 SubOpCode + ) +{ + EFI_STATUS Status; + UINT8 NodeOpCode; + UINT8 NodeSubOpCode; + + // Get the Node information. + Status = AmlGetObjectNodeInfo ( + ObjectNode, + &NodeOpCode, + &NodeSubOpCode, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // Check the OpCode and SubOpCode. + if ((OpCode != NodeOpCode) || + (SubOpCode != NodeSubOpCode)) { + return FALSE; + } + + return TRUE; +} + +/** Check whether DataNode has the input DataType. + + @param [in] DataNode Pointer to a data node. + @param [in] DataType DataType to check. + + @retval TRUE The node is a data node and + the DataType match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasDataType ( + IN AML_DATA_NODE_HANDLE DataNode, + IN EAML_NODE_DATA_TYPE DataType + ) +{ + EFI_STATUS Status; + EAML_NODE_DATA_TYPE NodeDataType; + + // Get the data type. + Status = AmlGetNodeDataType (DataNode, &NodeDataType); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // Check the data type. + if (NodeDataType != DataType) { + return FALSE; + } + + return TRUE; +} + +/** Check whether RdNode has the input RdDataType. + + @param [in] RdNode Pointer to a data node. + @param [in] RdDataType DataType to check. + + @retval TRUE The node is a Resource Data node and + the RdDataType match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasRdDataType ( + IN AML_DATA_NODE_HANDLE RdNode, + IN AML_RD_HEADER RdDataType + ) +{ + EFI_STATUS Status; + AML_RD_HEADER NodeRdDataType; + + // Get the resource data type. + Status = AmlGetResourceDataType ( + RdNode, + &NodeRdDataType + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // Check the RdDataType. + return AmlRdCompareDescId (&NodeRdDataType, RdDataType); +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h new file mode 100644 index 000000000..9872adddc --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h @@ -0,0 +1,93 @@ +/** @file + AML Helper. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_HELPER_H_ +#define AML_HELPER_H_ + +#include +#include + +/** Compare the NameString defined by the "Name ()" ASL function, + and stored in the NameOpNode, with the input NameString. + + An ASL NameString is expected to be NULL terminated, and can be composed + of NameSegs that have less that 4 chars, like "DEV". "DEV" will be expanded + as "DEV_". + + An AML NameString is not NULL terminated and is is only composed of + 4 chars long NameSegs. + + @param [in] NameOpNode NameOp object node defining a variable. + Must have an AML_NAME_OP/0 OpCode/SubOpCode. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] AslName ASL NameString to compare the NameOp's name with. + Must be NULL terminated. + + @retval TRUE If the AslName and the AmlName defined by the NameOp node + are similar. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNameOpCompareName ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN CHAR8 * AslName + ); + +/** Check whether ObjectNode has the input OpCode/SubOpcode couple. + + @param [in] ObjectNode Pointer to an object node. + @param [in] OpCode OpCode to check + @param [in] SubOpCode SubOpCode to check + + @retval TRUE The node is an object node and + the Opcode and SubOpCode match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasOpCode ( + IN AML_OBJECT_NODE_HANDLE ObjectNode, + IN UINT8 OpCode, + IN UINT8 SubOpCode + ); + +/** Check whether DataNode has the input DataType. + + @param [in] DataNode Pointer to a data node. + @param [in] DataType DataType to check. + + @retval TRUE The node is a data node and + the DataType match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasDataType ( + IN AML_DATA_NODE_HANDLE DataNode, + IN EAML_NODE_DATA_TYPE DataType + ); + +/** Check whether RdNode has the input RdDataType. + + @param [in] RdNode Pointer to a data node. + @param [in] RdDataType DataType to check. + + @retval TRUE The node is a Resource Data node and + the RdDataType match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasRdDataType ( + IN AML_DATA_NODE_HANDLE RdNode, + IN AML_RD_HEADER RdDataType + ); + +#endif // AML_HELPER_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c new file mode 100644 index 000000000..913c8dcdb --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c @@ -0,0 +1,320 @@ +/** @file + AML Update Resource Data. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +/* Even though this file has access to the internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. Only the external node + handle types should be used, i.e. AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, + etc. + Indeed, the functions in the "Api" folder should be implemented only + using the "safe" functions available in the "Include" folder. This + makes the functions available in the "Api" folder easy to export. +*/ +#include + +#include +#include +#include +#include + +/** Update the first interrupt of an Interrupt resource data node. + + The flags of the Interrupt resource data are left unchanged. + + The InterruptRdNode corresponds to the Resource Data created by the + "Interrupt ()" ASL macro. It is an Extended Interrupt Resource Data. + See ACPI 6.3 specification, s6.4.3.6 "Extended Interrupt Descriptor" + for more information about Extended Interrupt Resource Data. + + @param [in] InterruptRdNode Pointer to the an extended interrupt + resource data node. + @param [in] Irq Interrupt value to update. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of resources. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRdInterrupt ( + IN AML_DATA_NODE_HANDLE InterruptRdNode, + IN UINT32 Irq + ) +{ + EFI_STATUS Status; + UINT32 * FirstInterrupt; + UINT8 * QueryBuffer; + UINT32 QueryBufferSize; + + if ((InterruptRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)InterruptRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType ( + InterruptRdNode, + EAmlNodeDataTypeResourceData)) || + (!AmlNodeHasRdDataType ( + InterruptRdNode, + AML_RD_BUILD_LARGE_DESC_ID ( + ACPI_LARGE_EXTENDED_IRQ_DESCRIPTOR_NAME)))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + QueryBuffer = NULL; + + // Get the size of the InterruptRdNode buffer. + Status = AmlGetDataNodeBuffer ( + InterruptRdNode, + NULL, + &QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Check the Buffer is large enough. + if (QueryBufferSize < sizeof (EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Allocate a buffer to fetch the data. + QueryBuffer = AllocatePool (QueryBufferSize); + if (QueryBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + // Get the data. + Status = AmlGetDataNodeBuffer ( + InterruptRdNode, + QueryBuffer, + &QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + // Get the address of the first interrupt field. + FirstInterrupt = + ((EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR*)QueryBuffer)->InterruptNumber; + + *FirstInterrupt = Irq; + + // Update the InterruptRdNode buffer. + Status = AmlUpdateDataNode ( + InterruptRdNode, + EAmlNodeDataTypeResourceData, + QueryBuffer, + QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + +error_handler: + if (QueryBuffer != NULL) { + FreePool (QueryBuffer); + } + return Status; +} + +/** Update the interrupt list of an interrupt resource data node. + + The InterruptRdNode corresponds to the Resource Data created by the + "Interrupt ()" ASL function. It is an Extended Interrupt Resource Data. + See ACPI 6.3 specification, s6.4.3.6 "Extended Interrupt Descriptor" + for more information about Extended Interrupt Resource Data. + + @param [in] InterruptRdNode Pointer to the an extended interrupt + resource data node. + @param [in] ResourceConsumer The device consumes the specified interrupt + or produces it for use by a child device. + @param [in] EdgeTriggered The interrupt is edge triggered or + level triggered. + @param [in] ActiveLow The interrupt is active-high or active-low. + @param [in] Shared The interrupt can be shared with other + devices or not (Exclusive). + @param [in] IrqList Interrupt list. Must be non-NULL. + @param [in] IrqCount Interrupt count. Must be non-zero. + + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of resources. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRdInterruptEx ( + IN AML_DATA_NODE_HANDLE InterruptRdNode, + IN BOOLEAN ResourceConsumer, + IN BOOLEAN EdgeTriggered, + IN BOOLEAN ActiveLow, + IN BOOLEAN Shared, + IN UINT32 * IrqList, + IN UINT8 IrqCount + ) +{ + EFI_STATUS Status; + + EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR * RdInterrupt; + UINT32 * FirstInterrupt; + UINT8 * UpdateBuffer; + UINT16 UpdateBufferSize; + + if ((InterruptRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)InterruptRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType ( + InterruptRdNode, + EAmlNodeDataTypeResourceData)) || + (!AmlNodeHasRdDataType ( + InterruptRdNode, + AML_RD_BUILD_LARGE_DESC_ID ( + ACPI_LARGE_EXTENDED_IRQ_DESCRIPTOR_NAME))) || + (IrqList == NULL) || + (IrqCount == 0)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + UpdateBuffer = NULL; + UpdateBufferSize = sizeof (EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR) + + ((IrqCount - 1) * sizeof (UINT32)); + + // Allocate a buffer to update the data. + UpdateBuffer = AllocatePool (UpdateBufferSize); + if (UpdateBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + // Update the Resource Data information (structure size, interrupt count). + RdInterrupt = (EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR*)UpdateBuffer; + RdInterrupt->Header.Header.Byte = + AML_RD_BUILD_LARGE_DESC_ID (ACPI_LARGE_EXTENDED_IRQ_DESCRIPTOR_NAME); + RdInterrupt->Header.Length = + UpdateBufferSize - sizeof (ACPI_LARGE_RESOURCE_HEADER); + RdInterrupt->InterruptTableLength = IrqCount; + RdInterrupt->InterruptVectorFlags = (ResourceConsumer ? BIT0 : 0) | + (EdgeTriggered ? BIT1 : 0) | + (ActiveLow ? BIT2 : 0) | + (Shared ? BIT3 : 0); + + // Get the address of the first interrupt field. + FirstInterrupt = + ((EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR*)UpdateBuffer)->InterruptNumber; + + // Copy the input list of interrupts. + CopyMem (FirstInterrupt, IrqList, (sizeof (UINT32) * IrqCount)); + + // Update the InterruptRdNode buffer. + Status = AmlUpdateDataNode ( + InterruptRdNode, + EAmlNodeDataTypeResourceData, + UpdateBuffer, + UpdateBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + + // Cleanup + FreePool (UpdateBuffer); + + return Status; +} + +/** Update the base address and length of a QWord resource data node. + + @param [in] QWordRdNode Pointer a QWord resource data + node. + @param [in] BaseAddress Base address. + @param [in] BaseAddressLength Base address length. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of resources. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRdQWord ( + IN AML_DATA_NODE_HANDLE QWordRdNode, + IN UINT64 BaseAddress, + IN UINT64 BaseAddressLength + ) +{ + EFI_STATUS Status; + EFI_ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR * RdQWord; + + UINT8 * QueryBuffer; + UINT32 QueryBufferSize; + + if ((QWordRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)QWordRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType (QWordRdNode, EAmlNodeDataTypeResourceData)) || + (!AmlNodeHasRdDataType ( + QWordRdNode, + AML_RD_BUILD_LARGE_DESC_ID ( + ACPI_LARGE_QWORD_ADDRESS_SPACE_DESCRIPTOR_NAME)))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the size of the QWordRdNode's buffer. + Status = AmlGetDataNodeBuffer ( + QWordRdNode, + NULL, + &QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Allocate a buffer to fetch the data. + QueryBuffer = AllocatePool (QueryBufferSize); + if (QueryBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + // Get the data. + Status = AmlGetDataNodeBuffer ( + QWordRdNode, + QueryBuffer, + &QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + RdQWord = (EFI_ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR*)QueryBuffer; + + // Update the Base Address and Length. + RdQWord->AddrRangeMin = BaseAddress; + RdQWord->AddrRangeMax = BaseAddress + BaseAddressLength - 1; + RdQWord->AddrLen = BaseAddressLength; + + // Update Base Address Resource Data node. + Status = AmlUpdateDataNode ( + QWordRdNode, + EAmlNodeDataTypeResourceData, + QueryBuffer, + QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + +error_handler: + if (QueryBuffer != NULL) { + FreePool (QueryBuffer); + } + return Status; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c new file mode 100644 index 000000000..d6d9f5dfe --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c @@ -0,0 +1,701 @@ +/** @file + AML Code Generation. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +/** Utility function to link a node when returning from a CodeGen function. + + @param [in] Node Newly created node. + @param [in] ParentNode If provided, set ParentNode as the parent + of the node created. + @param [out] NewObjectNode If success, contains the created object node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +LinkNode ( + IN AML_OBJECT_NODE * Node, + IN AML_NODE_HEADER * ParentNode, + IN AML_OBJECT_NODE ** NewObjectNode + ) +{ + EFI_STATUS Status; + + if (NewObjectNode != NULL) { + *NewObjectNode = Node; + } + + // Add RdNode as the last element. + if (ParentNode != NULL) { + Status = AmlVarListAddTail (ParentNode, (AML_NODE_HEADER*)Node); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + return EFI_SUCCESS; +} + +/** AML code generation for DefinitionBlock. + + Create a Root Node handle. + It is the caller's responsibility to free the allocated memory + with the AmlDeleteTree function. + + AmlCodeGenDefinitionBlock (TableSignature, OemID, TableID, OEMRevision) is + equivalent to the following ASL code: + DefinitionBlock (AMLFileName, TableSignature, ComplianceRevision, + OemID, TableID, OEMRevision) {} + with the ComplianceRevision set to 2 and the AMLFileName is ignored. + + @param[in] TableSignature 4-character ACPI signature. + Must be 'DSDT' or 'SSDT'. + @param[in] OemId 6-character string OEM identifier. + @param[in] OemTableId 8-character string OEM table identifier. + @param[in] OemRevision OEM revision number. + @param[out] DefinitionBlockTerm The ASL Term handle representing a + Definition Block. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCodeGenDefinitionBlock ( + IN CONST CHAR8 * TableSignature, + IN CONST CHAR8 * OemId, + IN CONST CHAR8 * OemTableId, + IN UINT32 OemRevision, + OUT AML_ROOT_NODE ** NewRootNode + ) +{ + EFI_STATUS Status; + EFI_ACPI_DESCRIPTION_HEADER AcpiHeader; + + if ((TableSignature == NULL) || + (OemId == NULL) || + (OemTableId == NULL) || + (NewRootNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + CopyMem (&AcpiHeader.Signature, TableSignature, 4); + AcpiHeader.Length = sizeof (EFI_ACPI_DESCRIPTION_HEADER); + AcpiHeader.Revision = 2; + CopyMem (&AcpiHeader.OemId, OemId, 6); + CopyMem (&AcpiHeader.OemTableId, OemTableId, 8); + AcpiHeader.OemRevision = OemRevision; + AcpiHeader.CreatorId = TABLE_GENERATOR_CREATOR_ID_ARM; + AcpiHeader.CreatorRevision = CREATE_REVISION (1, 0); + + Status = AmlCreateRootNode (&AcpiHeader, NewRootNode); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** AML code generation for a String object node. + + @param [in] String Pointer to a NULL terminated string. + @param [out] NewObjectNode If success, contains the created + String object node. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlCodeGenString ( + IN CHAR8 * String, + OUT AML_OBJECT_NODE ** NewObjectNode + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE * ObjectNode; + AML_DATA_NODE * DataNode; + + if ((String == NULL) || + (NewObjectNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ObjectNode = NULL; + DataNode = NULL; + + Status = AmlCreateObjectNode ( + AmlGetByteEncodingByOpCode (AML_STRING_PREFIX, 0), + 0, + &ObjectNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlCreateDataNode ( + EAmlNodeDataTypeString, + (UINT8*)String, + (UINT32)AsciiStrLen (String) + 1, + &DataNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + Status = AmlSetFixedArgument ( + ObjectNode, + EAmlParseIndexTerm0, + (AML_NODE_HEADER*)DataNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + AmlDeleteTree ((AML_NODE_HEADER*)DataNode); + goto error_handler; + } + + *NewObjectNode = ObjectNode; + return Status; + +error_handler: + if (ObjectNode != NULL) { + AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode); + } + + return Status; +} + +/** AML code generation for an Integer object node. + + @param [in] Integer Integer of the Integer object node. + @param [out] NewObjectNode If success, contains the created + Integer object node. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlCodeGenInteger ( + IN UINT64 Integer, + OUT AML_OBJECT_NODE ** NewObjectNode + ) +{ + EFI_STATUS Status; + INT8 ValueWidthDiff; + + if (NewObjectNode == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Create an object node containing Zero. + Status = AmlCreateObjectNode ( + AmlGetByteEncodingByOpCode (AML_ZERO_OP, 0), + 0, + NewObjectNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Update the object node with integer value. + Status = AmlNodeSetIntegerValue (*NewObjectNode, Integer, &ValueWidthDiff); + if (EFI_ERROR (Status)) { + ASSERT (0); + AmlDeleteTree ((AML_NODE_HEADER*)*NewObjectNode); + } + + return Status; +} + +/** AML code generation for a Name object node. + + @param [in] NameString The new variable name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + This input string is copied. + @param [in] Object Object associated to the NameString. + @param [in] ParentNode If provided, set ParentNode as the parent + of the node created. + @param [out] NewObjectNode If success, contains the created node. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlCodeGenName ( + IN CONST CHAR8 * NameString, + IN AML_OBJECT_NODE * Object, + IN AML_NODE_HEADER * ParentNode, OPTIONAL + OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE * ObjectNode; + AML_DATA_NODE * DataNode; + CHAR8 * AmlNameString; + UINT32 AmlNameStringSize; + + if ((NameString == NULL) || + (Object == NULL) || + ((ParentNode == NULL) && (NewObjectNode == NULL))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ObjectNode = NULL; + DataNode = NULL; + AmlNameString = NULL; + + Status = ConvertAslNameToAmlName (NameString, &AmlNameString); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlGetNameStringSize (AmlNameString, &AmlNameStringSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler1; + } + + Status = AmlCreateObjectNode ( + AmlGetByteEncodingByOpCode (AML_NAME_OP, 0), + 0, + &ObjectNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler1; + } + + Status = AmlCreateDataNode ( + EAmlNodeDataTypeNameString, + (UINT8*)AmlNameString, + AmlNameStringSize, + &DataNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler2; + } + + Status = AmlSetFixedArgument ( + ObjectNode, + EAmlParseIndexTerm0, + (AML_NODE_HEADER*)DataNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + AmlDeleteTree ((AML_NODE_HEADER*)DataNode); + goto error_handler2; + } + + Status = AmlSetFixedArgument ( + ObjectNode, + EAmlParseIndexTerm1, + (AML_NODE_HEADER*)Object + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler2; + } + + Status = LinkNode ( + ObjectNode, + ParentNode, + NewObjectNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler2; + } + + // Free AmlNameString before returning as it is copied + // in the call to AmlCreateDataNode(). + goto error_handler1; + +error_handler2: + if (ObjectNode != NULL) { + AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode); + } + +error_handler1: + if (AmlNameString != NULL) { + FreePool (AmlNameString); + } + + return Status; +} + +/** AML code generation for a Name object node, containing a String. + + AmlCodeGenNameString ("_HID", "HID0000", ParentNode, NewObjectNode) is + equivalent of the following ASL code: + Name(_HID, "HID0000") + + @param [in] NameString The new variable name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + The input string is copied. + @param [in] String NULL terminated String to associate to the + NameString. + @param [in] ParentNode If provided, set ParentNode as the parent + of the node created. + @param [out] NewObjectNode If success, contains the created node. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCodeGenNameString ( + IN CONST CHAR8 * NameString, + IN CHAR8 * String, + IN AML_NODE_HEADER * ParentNode, OPTIONAL + OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE * ObjectNode; + + if ((NameString == NULL) || + (String == NULL) || + ((ParentNode == NULL) && (NewObjectNode == NULL))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlCodeGenString (String, &ObjectNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlCodeGenName ( + NameString, + ObjectNode, + ParentNode, + NewObjectNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode); + } + + return Status; +} + +/** AML code generation for a Name object node, containing an Integer. + + AmlCodeGenNameInteger ("_UID", 1, ParentNode, NewObjectNode) is + equivalent of the following ASL code: + Name(_UID, One) + + @param [in] NameString The new variable name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + The input string is copied. + @param [in] Integer Integer to associate to the NameString. + @param [in] ParentNode If provided, set ParentNode as the parent + of the node created. + @param [out] NewObjectNode If success, contains the created node. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCodeGenNameInteger ( + IN CONST CHAR8 * NameString, + IN UINT64 Integer, + IN AML_NODE_HEADER * ParentNode, OPTIONAL + OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE * ObjectNode; + + if ((NameString == NULL) || + ((ParentNode == NULL) && (NewObjectNode == NULL))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlCodeGenInteger (Integer, &ObjectNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlCodeGenName ( + NameString, + ObjectNode, + ParentNode, + NewObjectNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode); + } + + return Status; +} + +/** AML code generation for a Device object node. + + AmlCodeGenDevice ("COM0", ParentNode, NewObjectNode) is + equivalent of the following ASL code: + Device(COM0) {} + + @param [in] NameString The new Device's name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + The input string is copied. + @param [in] ParentNode If provided, set ParentNode as the parent + of the node created. + @param [out] NewObjectNode If success, contains the created node. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCodeGenDevice ( + IN CONST CHAR8 * NameString, + IN AML_NODE_HEADER * ParentNode, OPTIONAL + OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE * ObjectNode; + AML_DATA_NODE * DataNode; + CHAR8 * AmlNameString; + UINT32 AmlNameStringSize; + + if ((NameString == NULL) || + ((ParentNode == NULL) && (NewObjectNode == NULL))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ObjectNode = NULL; + DataNode = NULL; + AmlNameString = NULL; + + Status = ConvertAslNameToAmlName (NameString, &AmlNameString); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlGetNameStringSize (AmlNameString, &AmlNameStringSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler1; + } + + Status = AmlCreateObjectNode ( + AmlGetByteEncodingByOpCode (AML_EXT_OP, AML_EXT_DEVICE_OP), + AmlNameStringSize + AmlComputePkgLengthWidth (AmlNameStringSize), + &ObjectNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler1; + } + + Status = AmlCreateDataNode ( + EAmlNodeDataTypeNameString, + (UINT8*)AmlNameString, + AmlNameStringSize, + &DataNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler2; + } + + Status = AmlSetFixedArgument ( + ObjectNode, + EAmlParseIndexTerm0, + (AML_NODE_HEADER*)DataNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + AmlDeleteTree ((AML_NODE_HEADER*)DataNode); + goto error_handler2; + } + + Status = LinkNode ( + ObjectNode, + ParentNode, + NewObjectNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler2; + } + + // Free AmlNameString before returning as it is copied + // in the call to AmlCreateDataNode(). + goto error_handler1; + +error_handler2: + if (ObjectNode != NULL) { + AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode); + } + +error_handler1: + if (AmlNameString != NULL) { + FreePool (AmlNameString); + } + + return Status; +} + +/** AML code generation for a Scope object node. + + AmlCodeGenScope ("_SB", ParentNode, NewObjectNode) is + equivalent of the following ASL code: + Scope(_SB) {} + + @param [in] NameString The new Scope's name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + The input string is copied. + @param [in] ParentNode If provided, set ParentNode as the parent + of the node created. + @param [out] NewObjectNode If success, contains the created node. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCodeGenScope ( + IN CONST CHAR8 * NameString, + IN AML_NODE_HEADER * ParentNode, OPTIONAL + OUT AML_OBJECT_NODE ** NewObjectNode OPTIONAL + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE * ObjectNode; + AML_DATA_NODE * DataNode; + CHAR8 * AmlNameString; + UINT32 AmlNameStringSize; + + if ((NameString == NULL) || + ((ParentNode == NULL) && (NewObjectNode == NULL))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ObjectNode = NULL; + DataNode = NULL; + AmlNameString = NULL; + + Status = ConvertAslNameToAmlName (NameString, &AmlNameString); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlGetNameStringSize (AmlNameString, &AmlNameStringSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler1; + } + + Status = AmlCreateObjectNode ( + AmlGetByteEncodingByOpCode (AML_SCOPE_OP, 0), + AmlNameStringSize + AmlComputePkgLengthWidth (AmlNameStringSize), + &ObjectNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler1; + } + + Status = AmlCreateDataNode ( + EAmlNodeDataTypeNameString, + (UINT8*)AmlNameString, + AmlNameStringSize, + &DataNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler2; + } + + Status = AmlSetFixedArgument ( + ObjectNode, + EAmlParseIndexTerm0, + (AML_NODE_HEADER*)DataNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + AmlDeleteTree ((AML_NODE_HEADER*)DataNode); + goto error_handler2; + } + + Status = LinkNode ( + ObjectNode, + ParentNode, + NewObjectNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler2; + } + + // Free AmlNameString before returning as it is copied + // in the call to AmlCreateDataNode(). + goto error_handler1; + +error_handler2: + if (ObjectNode != NULL) { + AmlDeleteTree ((AML_NODE_HEADER*)ObjectNode); + } + +error_handler1: + if (AmlNameString != NULL) { + FreePool (AmlNameString); + } + + return Status; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c new file mode 100644 index 000000000..9e7a508e6 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.c @@ -0,0 +1,256 @@ +/** @file + AML Resource Data Code Generation. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Glossary: + - Rd or RD - Resource Data + - Rds or RDS - Resource Data Small + - Rdl or RDL - Resource Data Large +**/ + +#include +#include + +#include +#include +#include +#include +#include + +/** If ParentNode is not NULL, append RdNode. + If NewRdNode is not NULL, update its value to RdNode. + + @param [in] RdNode Newly created Resource Data node. + @param [in] ParentNode If not NULL, add the generated node + to the end of the variable list of + argument of the ParentNode, but + before the "End Tag" Resource Data. + Must be a BufferOpNode. + @param [out] NewRdNode If not NULL, update the its value to RdNode. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +LinkRdNode ( + IN AML_DATA_NODE * RdNode, + IN AML_OBJECT_NODE * ParentNode, + IN AML_DATA_NODE ** NewRdNode + ) +{ + EFI_STATUS Status; + EFI_STATUS Status1; + + if (NewRdNode != NULL) { + *NewRdNode = RdNode; + } + + // Add RdNode as the last element, but before the EndTag. + if (ParentNode != NULL) { + Status = AmlAppendRdNode (ParentNode, RdNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + Status1 = AmlDeleteTree ((AML_NODE_HEADER*)RdNode); + ASSERT_EFI_ERROR (Status1); + // Return original error. + return Status; + } + } + + return EFI_SUCCESS; +} + +/** Code generation for the "Interrupt ()" ASL function. + + This function creates a Resource Data element corresponding to the + "Interrupt ()" ASL function and stores it in an AML Data Node. + + The Resource Data effectively created is an Extended Interrupt Resource + Data. See ACPI 6.3 specification, s6.4.3.6 "Extended Interrupt Descriptor" + for more information about Extended Interrupt Resource Data. + + This function allocates memory to create a data node. It is the caller's + responsibility to either: + - attach this node to an AML tree; + - delete this node. + + @param [in] ResourceConsumer The device consumes the specified interrupt + or produces it for use by a child device. + @param [in] EdgeTriggered The interrupt is edge triggered or + level triggered. + @param [in] ActiveLow The interrupt is active-high or active-low. + @param [in] Shared The interrupt can be shared with other + devices or not (Exclusive). + @param [in] IrqList Interrupt list. Must be non-NULL. + @param [in] IrqCount Interrupt count. Must be non-zero. + @param [in] ParentNode If not NULL, add the generated node + to the end of the variable list of + argument of the ParentNode, but + before the "End Tag" Resource Data. + Must be a BufferOpNode. + @param [out] NewRdNode If success, contains the generated 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 +AmlCodeGenInterrupt ( + IN BOOLEAN ResourceConsumer, + IN BOOLEAN EdgeTriggered, + IN BOOLEAN ActiveLow, + IN BOOLEAN Shared, + IN UINT32 * IrqList, + IN UINT8 IrqCount, + IN AML_OBJECT_NODE * ParentNode, OPTIONAL + OUT AML_DATA_NODE ** NewRdNode OPTIONAL + ) +{ + EFI_STATUS Status; + + AML_DATA_NODE * RdNode; + EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR RdInterrupt; + UINT32 * FirstInterrupt; + + if ((IrqList == NULL) || + (IrqCount == 0) || + ((ParentNode == NULL) && (NewRdNode == NULL))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + RdInterrupt.Header.Header.Bits.Name = + ACPI_LARGE_EXTENDED_IRQ_DESCRIPTOR_NAME; + RdInterrupt.Header.Header.Bits.Type = ACPI_LARGE_ITEM_FLAG; + RdInterrupt.Header.Length = sizeof (EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR) - + sizeof (ACPI_LARGE_RESOURCE_HEADER); + RdInterrupt.InterruptVectorFlags = (ResourceConsumer ? BIT0 : 0) | + (EdgeTriggered ? BIT1 : 0) | + (ActiveLow ? BIT2 : 0) | + (Shared ? BIT3 : 0); + RdInterrupt.InterruptTableLength = IrqCount; + + // Get the address of the first interrupt field. + FirstInterrupt = RdInterrupt.InterruptNumber; + + // Copy the list of interrupts. + CopyMem (FirstInterrupt, IrqList, (sizeof (UINT32) * IrqCount)); + + Status = AmlCreateDataNode ( + EAmlNodeDataTypeResourceData, + (UINT8*)&RdInterrupt, + sizeof (EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR), + &RdNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + return LinkRdNode (RdNode, ParentNode, NewRdNode); +} + +/** Add an Interrupt Resource Data node. + + This function creates a Resource Data element corresponding to the + "Interrupt ()" ASL function, stores it in an AML Data Node. + + It then adds it after the input CurrRdNode in the list of resource data + element. + + The Resource Data effectively created is an Extended Interrupt Resource + Data. See ACPI 6.3 specification, s6.4.3.6 "Extended Interrupt Descriptor" + for more information about Extended Interrupt Resource Data. + + The Extended Interrupt contains one single interrupt. + + This function allocates memory to create a data node. It is the caller's + responsibility to either: + - attach this node to an AML tree; + - delete this node. + + Note: The _CRS node must be defined using the ASL Name () function. + e.g. Name (_CRS, ResourceTemplate () { + ... + } + + @param [in] NameOpCrsNode NameOp object node defining a "_CRS" object. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] ResourceConsumer The device consumes the specified interrupt + or produces it for use by a child device. + @param [in] EdgeTriggered The interrupt is edge triggered or + level triggered. + @param [in] ActiveLow The interrupt is active-high or active-low. + @param [in] Shared The interrupt can be shared with other + devices or not (Exclusive). + @param [in] IrqList Interrupt list. Must be non-NULL. + @param [in] IrqCount Interrupt count. Must be non-zero. + + + @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 +AmlCodeGenCrsAddRdInterrupt ( + IN AML_OBJECT_NODE_HANDLE NameOpCrsNode, + IN BOOLEAN ResourceConsumer, + IN BOOLEAN EdgeTriggered, + IN BOOLEAN ActiveLow, + IN BOOLEAN Shared, + IN UINT32 * IrqList, + IN UINT8 IrqCount + ) +{ + EFI_STATUS Status; + + AML_OBJECT_NODE_HANDLE BufferOpNode; + + if ((IrqList == NULL) || + (IrqCount == 0) || + (!AmlNodeHasOpCode (NameOpCrsNode, AML_NAME_OP, 0)) || + (!AmlNameOpCompareName (NameOpCrsNode, "_CRS"))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the _CRS value which is represented as a BufferOp object node + // which is the 2nd fixed argument (i.e. index 1). + BufferOpNode = (AML_OBJECT_NODE_HANDLE)AmlGetFixedArgument ( + NameOpCrsNode, + EAmlParseIndexTerm1 + ); + if ((BufferOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)BufferOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (BufferOpNode, AML_BUFFER_OP, 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Generate the Extended Interrupt Resource Data node, + // and attach it as the last variable argument of the BufferOpNode. + Status = AmlCodeGenInterrupt ( + ResourceConsumer, + EdgeTriggered, + ActiveLow, + Shared, + IrqList, + IrqCount, + BufferOpNode, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + + return Status; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h new file mode 100644 index 000000000..08364db44 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlResourceDataCodeGen.h @@ -0,0 +1,59 @@ +/** @file + AML Resource Data Code Generation. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_RESOURCE_DATA_CODE_GEN_H_ +#define AML_RESOURCE_DATA_CODE_GEN_H_ + +/** Code generation for the "Interrupt ()" ASL function. + + This function creates a Resource Data element corresponding to the + "Interrupt ()" ASL function and stores it in an AML Data Node. + + The Resource Data effectively created is an Extended Interrupt Resource + Data. See ACPI 6.3 specification, s6.4.3.6 "Extended Interrupt Descriptor" + for more information about Extended Interrupt Resource Data. + + This function allocates memory to create a data node. It is the caller's + responsibility to either: + - attach this node to an AML tree; + - delete this node. + + @param [in] ResourceConsumer The device consumes the specified interrupt + or produces it for use by a child device. + @param [in] EdgeTriggered The interrupt is edge triggered or + level triggered. + @param [in] ActiveLow The interrupt is active-high or active-low. + @param [in] Shared The interrupt can be shared with other + devices or not (Exclusive). + @param [in] IrqList Interrupt list. Must be non-NULL. + @param [in] IrqCount Interrupt count. Must be non-zero. + @param [in] ParentNode If not NULL, add the generated node + to the end of the variable list of + argument of the ParentNode, but + before the "End Tag" Resource Data. + Must be a BufferOpNode. + @param [out] NewRdNode If success, contains the generated 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 +AmlCodeGenInterrupt ( + IN BOOLEAN ResourceConsumer, + IN BOOLEAN EdgeTriggered, + IN BOOLEAN ActiveLow, + IN BOOLEAN Shared, + IN UINT32 * IrqList, + IN UINT8 IrqCount, + IN AML_OBJECT_NODE * ParentNode, OPTIONAL + OUT AML_DATA_NODE ** NewRdNode OPTIONAL + ); + +#endif // AML_RESOURCE_DATA_CODE_GEN_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c new file mode 100644 index 000000000..dc3737489 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c @@ -0,0 +1,1501 @@ +/** @file + AML NameSpace. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + 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 + +#include +#include +#include +#include +#include +#include + +/** 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; + CHAR8 * SearchedPath; + + AML_STREAM * CurrNodePathBStream; + CHAR8 * CurrNodePath; + 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; + } + + SearchedPath = (CHAR8*)AmlStreamGetCurrPos (SearchPathBStream); + CurrNodePath = (CHAR8*)AmlStreamGetCurrPos (CurrNodePathBStream); + + // 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:")); + AmlDbgPrintChars ( + DEBUG_VERBOSE, + (CHAR8*)AmlStreamGetCurrPos (SearchPathBStream), + AmlStreamGetIndex (SearchPathBStream) + ); + DEBUG ((DEBUG_VERBOSE, "\nPath of the current node: ")); + AmlDbgPrintChars ( + 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 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 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; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h new file mode 100644 index 000000000..3d8ebc8b1 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h @@ -0,0 +1,74 @@ +/** @file + AML NameSpace. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_NAMESPACE_H_ +#define AML_NAMESPACE_H_ + +#include +#include + +/** 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 + ); + +/** 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 + ); + +#endif // AML_NAMESPACE_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c new file mode 100644 index 000000000..c25ee22dc --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c @@ -0,0 +1,375 @@ +/** @file + AML Field List Parser. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include +#include +#include +#include + +/** Parse a field element. + + The field elements this function can parse are one of: + - ReservedField; + - AccessField; + - ConnectField; + - ExtendedAccessField. + Indeed, the NamedField field element doesn't have an OpCode. Thus it needs + to be parsed differently. + + @param [in] FieldByteEncoding Field byte encoding to parse. + @param [in, out] FieldNode Field node to attach the field + element to. + Must have the AML_HAS_FIELD_LIST + attribute. + @param [in, out] FStream Forward stream pointing to a field + element not being a named field. + The stream must not be at its end. + @param [in, out] NameSpaceRefList List of namespace reference nodes, + allowing to associate an absolute + path to a node in the tree. + + @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. +*/ +STATIC +EFI_STATUS +EFIAPI +AmlParseFieldElement ( + IN CONST AML_BYTE_ENCODING * FieldByteEncoding, + IN OUT AML_OBJECT_NODE * FieldNode, + IN OUT AML_STREAM * FStream, + IN OUT LIST_ENTRY * NameSpaceRefList + ) +{ + EFI_STATUS Status; + + UINT8 * CurrPos; + AML_OBJECT_NODE * NewNode; + + UINT32 PkgLenOffset; + UINT32 PkgLenSize; + + // Check whether the node is an Object Node and has a field list. + // The byte encoding must be a field element. + if ((FieldByteEncoding == NULL) || + ((FieldByteEncoding->Attribute & AML_IS_FIELD_ELEMENT) == 0) || + ((FieldByteEncoding->Attribute & AML_IS_PSEUDO_OPCODE) == + AML_IS_PSEUDO_OPCODE) || + !AmlNodeHasAttribute (FieldNode, AML_HAS_FIELD_LIST) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (NameSpaceRefList == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + CurrPos = AmlStreamGetCurrPos (FStream); + if (CurrPos == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Skip the field opcode (1 byte) as it is already in the FieldByteEncoding. + DumpRaw (CurrPos, 1); + Status = AmlStreamProgress (FStream, 1); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + CurrPos = AmlStreamGetCurrPos (FStream); + if (CurrPos == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Parse the PkgLen if available. + PkgLenSize = 0; + if ((FieldByteEncoding->Attribute & AML_HAS_PKG_LENGTH) == + AML_HAS_PKG_LENGTH) { + PkgLenOffset = AmlGetPkgLength (CurrPos, &PkgLenSize); + if (PkgLenOffset == 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Move stream forward as the PkgLen has been read. + DumpRaw (CurrPos, PkgLenOffset); + Status = AmlStreamProgress (FStream, PkgLenOffset); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Update the current position as PkgLen has been parsed. + CurrPos = AmlStreamGetCurrPos (FStream); + } + + Status = AmlCreateObjectNode ( + FieldByteEncoding, + PkgLenSize, + &NewNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Add the FieldElement to the Variable Argument List. + Status = AmlVarListAddTailInternal ( + (AML_NODE_HEADER*)FieldNode, + (AML_NODE_HEADER*)NewNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + // Delete the sub-tree if the insertion failed. + // Otherwise its reference will be lost. + AmlDeleteTree ((AML_NODE_HEADER*)NewNode); + return Status; + } + + // Some field elements do not have fixed arguments. + if (!IS_END_OF_STREAM (FStream)) { + // Parse the fixed arguments of the field element. + Status = AmlParseFixedArguments ( + NewNode, + FStream, + NameSpaceRefList + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + } + + return Status; +} + +/** Parse a named field element. + + Indeed, the NamedField field element doesn't have an OpCode. Thus it needs + to be parsed differently. NamedField field element start with a char. + + @param [in] NamedFieldByteEncoding Field byte encoding to parse. + @param [in, out] FieldNode Field node to attach the field + element to. + Must have the AML_HAS_FIELD_LIST + attribute. + @param [in, out] FStream Forward stream pointing to a named + field element. + The stream must not be at its end. + @param [in, out] NameSpaceRefList List of namespace reference nodes, + allowing to associate an absolute + path to a node in the tree. + + @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. +*/ +STATIC +EFI_STATUS +EFIAPI +AmlParseNamedFieldElement ( + IN CONST AML_BYTE_ENCODING * NamedFieldByteEncoding, + IN OUT AML_OBJECT_NODE * FieldNode, + IN OUT AML_STREAM * FStream, + IN OUT LIST_ENTRY * NameSpaceRefList +) +{ + EFI_STATUS Status; + AML_OBJECT_NODE * NewNode; + + // Check whether the node is an Object Node and has a field list. + // The byte encoding must be a char. + if ((NamedFieldByteEncoding == NULL) || + ((NamedFieldByteEncoding->Attribute & AML_IS_NAME_CHAR) == 0) || + !AmlNodeHasAttribute (FieldNode, AML_HAS_FIELD_LIST) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (NameSpaceRefList == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Create a NamedField node. + Status = AmlCreateObjectNode ( + AmlGetFieldEncodingByOpCode (AML_FIELD_NAMED_OP, 0), + 0, + &NewNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Add the NamedField node to the variable argument list. + Status = AmlVarListAddTailInternal ( + (AML_NODE_HEADER*)FieldNode, + (AML_NODE_HEADER*)NewNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + // Delete the sub-tree if the insertion failed. + // Otherwise its reference will be lost. + AmlDeleteTree ((AML_NODE_HEADER*)NewNode); + return Status; + } + + // Parse the fixed arguments: [0]NameSeg, [1]PkgLen. + Status = AmlParseFixedArguments ( + NewNode, + FStream, + NameSpaceRefList + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Add the NamedField to the namespace reference list. + Status = AmlAddNameSpaceReference ( + NewNode, + NameSpaceRefList + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Parse the FieldList contained in the stream. + + Create an object node for each field element parsed in the field list + available in the Stream, and add them to the variable list of arguments + of the FieldNode. + + Nodes that can have a field list are referred as field nodes. They have the + AML_HAS_FIELD_LIST attribute. + + According to the ACPI 6.3 specification, s20.2.5.2 "Named Objects Encoding", + field elements can be: + - NamedField := NameSeg PkgLength; + - ReservedField := 0x00 PkgLength; + - AccessField := 0x01 AccessType AccessAttrib; + - ConnectField := <0x02 NameString> | <0x02 BufferData>; + - ExtendedAccessField := 0x03 AccessType ExtendedAccessAttrib AccessLength. + + A small set of opcodes describes the field elements. They are referred as + field opcodes. An AML_BYTE_ENCODING table has been created for field OpCodes. + Field elements: + - don't have a SubOpCode; + - have at most 3 fixed arguments (as opposed to 6 for standard AML objects); + - don't have a variable list of arguments; + - only the NamedField field element is part of the AML namespace. + + ConnectField's BufferData is a buffer node containing a single + resource data element. + NamedField field elements don't have an AML OpCode. NameSeg starts with a + Char type and can thus be differentiated from the Opcodes for other fields. + A pseudo OpCode has been created to simplify the parser. + + The branch created from parsing a field node is as: + (FieldNode) + \ + |- [FixedArg[0]][FixedArg[1]] # Fixed Arguments + |- {(FieldElement[0])->(FieldElement[1])->...)} # Variable Arguments + + With FieldElement[n] being one of NamedField, ReservedField, AccessField, + ConnectField, ExtendedAccessField. + + @param [in] FieldNode Field node. + Must have the AML_HAS_FIELD_LIST + attribute. + @param [in] FStream Forward stream pointing to a field list. + The stream must not be at its end. + @param [in] NameSpaceRefList List of namespace reference nodes, + allowing to associate an absolute + path to a node in the tree. + + @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 +AmlParseFieldList ( + IN AML_OBJECT_NODE * FieldNode, + IN AML_STREAM * FStream, + IN LIST_ENTRY * NameSpaceRefList + ) +{ + EFI_STATUS Status; + + UINT8 * CurrPos; + CONST AML_BYTE_ENCODING * FieldByteEncoding; + CONST AML_BYTE_ENCODING * NamedFieldByteEncoding; + + // Check whether the node is an Object Node and has a field list. + if (!AmlNodeHasAttribute (FieldNode, AML_HAS_FIELD_LIST) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (NameSpaceRefList == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Iterate through the field elements, creating nodes + // and adding them to the variable list of elements of Node. + while (!IS_END_OF_STREAM (FStream)) { + CurrPos = AmlStreamGetCurrPos (FStream); + + // Check for a field opcode. + FieldByteEncoding = AmlGetFieldEncoding (CurrPos); + if (FieldByteEncoding != NULL) { + Status = AmlParseFieldElement ( + FieldByteEncoding, + FieldNode, + FStream, + NameSpaceRefList + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } else { + // Handle the case of Pseudo OpCodes. + // NamedField has a Pseudo OpCode and starts with a NameChar. Therefore, + // call AmlGetByteEncoding() to check that the encoding is NameChar. + NamedFieldByteEncoding = AmlGetByteEncoding (CurrPos); + if ((NamedFieldByteEncoding != NULL) && + (NamedFieldByteEncoding->Attribute & AML_IS_NAME_CHAR)) { + // This is a NamedField field element since it is starting with a char. + Status = AmlParseNamedFieldElement ( + NamedFieldByteEncoding, + FieldNode, + FStream, + NameSpaceRefList + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } else { + // A field opcode or an AML byte encoding is expected. + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } + } // while + + return EFI_SUCCESS; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.h new file mode 100644 index 000000000..576f6c41d --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.h @@ -0,0 +1,77 @@ +/** @file + AML Field List. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_FIELD_LIST_PARSER_H_ +#define AML_FIELD_LIST_PARSER_H_ + +#include +#include + +/** Parse the FieldList contained in the stream. + + Create an object node for each field element parsed in the field list + available in the Stream, and add them to the variable list of arguments + of the FieldNode. + + Nodes that can have a field list are referred as field nodes. They have the + AML_HAS_FIELD_LIST attribute. + + According to the ACPI 6.3 specification, s20.2.5.2 "Named Objects Encoding", + field elements can be: + - NamedField := NameSeg PkgLength; + - ReservedField := 0x00 PkgLength; + - AccessField := 0x01 AccessType AccessAttrib; + - ConnectField := <0x02 NameString> | <0x02 BufferData>; + - ExtendedAccessField := 0x03 AccessType ExtendedAccessAttrib AccessLength. + + A small set of opcodes describes the field elements. They are referred as + field opcodes. An AML_BYTE_ENCODING table has been created for field OpCodes. + Field elements: + - don't have a SubOpCode; + - have at most 3 fixed arguments (as opposed to 6 for standard AML objects); + - don't have a variable list of arguments; + - only the NamedField field element is part of the AML namespace. + + ConnectField's BufferData is a buffer node containing a single + resource data element. + NamedField field elements don't have an AML OpCode. NameSeg starts with a + Char type and can thus be differentiated from the Opcodes for other fields. + A pseudo OpCode has been created to simplify the parser. + + The branch created from parsing a field node is as: + (FieldNode) + \ + |- [FixedArg[0]][FixedArg[1]] # Fixed Arguments + |- {(FieldElement[0])->(FieldElement[1])->...)} # Variable Arguments + + With FieldElement[n] being one of NamedField, ReservedField, AccessField, + ConnectField, ExtendedAccessField. + + @param [in] FieldNode Field node. + Must have the AML_HAS_FIELD_LIST + attribute. + @param [in] FStream Forward stream pointing to a field list. + The stream must not be at its end. + @param [in] NameSpaceRefList List of namespace reference nodes, + allowing to associate an absolute + path to a node in the tree. + + @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 +AmlParseFieldList ( + IN AML_OBJECT_NODE * FieldNode, + IN AML_STREAM * FStream, + IN LIST_ENTRY * NameSpaceRefList + ); + +#endif // AML_FIELD_LIST_PARSER_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c new file mode 100644 index 000000000..bc98d950b --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.c @@ -0,0 +1,1458 @@ +/** @file + AML Method Parser. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +/** Delete a namespace reference node and its pathname. + + It is the caller's responsibility to check the NameSpaceRefNode has been + removed from any list the node is part of. + + @param [in] NameSpaceRefNode Pointer to an AML_NAMESPACE_REF_NODE. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlDeleteNameSpaceRefNode ( + IN AML_NAMESPACE_REF_NODE * NameSpaceRefNode + ) +{ + if (NameSpaceRefNode == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (NameSpaceRefNode->RawAbsolutePath != NULL) { + FreePool ((CHAR8*)NameSpaceRefNode->RawAbsolutePath); + } else { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + FreePool (NameSpaceRefNode); + return EFI_SUCCESS; +} + +/** Delete a list of namespace reference nodes. + + @param [in] NameSpaceRefList List of namespace reference nodes. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeleteNameSpaceRefList ( + IN LIST_ENTRY * NameSpaceRefList + ) +{ + EFI_STATUS Status; + LIST_ENTRY * CurrentLink; + + if (NameSpaceRefList == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + while (!IsListEmpty (NameSpaceRefList)) { + CurrentLink = NameSpaceRefList->ForwardLink; + RemoveEntryList (CurrentLink); + Status = AmlDeleteNameSpaceRefNode ( + (AML_NAMESPACE_REF_NODE*)CurrentLink + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } // while + + return EFI_SUCCESS; +} + +/** Create an AML_NAMESPACE_REF_NODE. + + A Buffer is allocated to store the raw AML absolute path. + + @param [in] ObjectNode Node being part of the namespace. + Must be have the AML_IN_NAMESPACE + attribute. + @param [in] RawAbsolutePath AML raw absolute path of the ObjectNode. + A raw NameString is a concatenated list + of 4 chars long names. + @param [in] RawAbsolutePathSize Size of the RawAbsolutePath buffer. + @param [out] NameSpaceRefNodePtr The created AML_METHOD_REF_NODE. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlCreateMethodRefNode ( + IN CONST AML_OBJECT_NODE * ObjectNode, + IN CONST CHAR8 * RawAbsolutePath, + IN UINT32 RawAbsolutePathSize, + OUT AML_NAMESPACE_REF_NODE ** NameSpaceRefNodePtr + ) +{ + AML_NAMESPACE_REF_NODE * NameSpaceRefNode; + + if (!AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE) || + (RawAbsolutePath == NULL) || + (RawAbsolutePathSize == 0) || + (NameSpaceRefNodePtr == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + NameSpaceRefNode = AllocateZeroPool (sizeof (AML_NAMESPACE_REF_NODE)); + if (NameSpaceRefNode == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + NameSpaceRefNode->RawAbsolutePathSize = RawAbsolutePathSize; + NameSpaceRefNode->RawAbsolutePath = AllocateCopyPool ( + RawAbsolutePathSize, + RawAbsolutePath + ); + if (NameSpaceRefNode->RawAbsolutePath == NULL) { + FreePool (NameSpaceRefNode); + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + InitializeListHead (&NameSpaceRefNode->Link); + + NameSpaceRefNode->NodeRef = ObjectNode; + *NameSpaceRefNodePtr = NameSpaceRefNode; + + return EFI_SUCCESS; +} + +#if !defined (MDEPKG_NDEBUG) + +/** Print the list of raw absolute paths of the NameSpace reference list. + + @param [in] NameSpaceRefList List of NameSpace reference nodes. +**/ +VOID +EFIAPI +AmlDbgPrintNameSpaceRefList ( + IN CONST LIST_ENTRY * NameSpaceRefList + ) +{ + LIST_ENTRY * CurrLink; + AML_NAMESPACE_REF_NODE * CurrNameSpaceNode; + + if (NameSpaceRefList == NULL) { + ASSERT (0); + return; + } + + DEBUG ((DEBUG_INFO, "AmlMethodParser: List of available raw AML paths:\n")); + + CurrLink = NameSpaceRefList->ForwardLink; + while (CurrLink != NameSpaceRefList) { + CurrNameSpaceNode = (AML_NAMESPACE_REF_NODE*)CurrLink; + + AmlDbgPrintChars ( + DEBUG_INFO, + CurrNameSpaceNode->RawAbsolutePath, + CurrNameSpaceNode->RawAbsolutePathSize + ); + DEBUG ((DEBUG_INFO, "\n")); + + CurrLink = CurrLink->ForwardLink; + } + + DEBUG ((DEBUG_INFO, "\n")); +} + +#endif // MDEPKG_NDEBUG + +/** From a forward stream pointing to a NameString, + initialize a raw backward stream. + + StartOfStream + Fstream: CurrPos EndOfStream + v v + +-----------------------------------------+ + |^^^[Multi-name prefix]AAAA.BBBB.CCCC | + +-----------------------------------------+ + ^ ^ + RawPathNameBStream: EndOfStream CurrPos + StartOfStream + + No memory is allocated when initializing the stream. + + @param [in] FStream Forward stream pointing to a NameString. + The stream must not be at its end. + @param [out] RawPathNameBStream Backward stream containing the + raw AML path. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlInitRawPathBStream ( + IN CONST AML_STREAM * FStream, + OUT AML_STREAM * RawPathNameBStream + ) +{ + EFI_STATUS Status; + + UINT8 * RawPathBuffer; + CONST CHAR8 * Buffer; + + UINT32 Root; + UINT32 ParentPrefix; + UINT32 SegCount; + + if (!IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (RawPathNameBStream == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Buffer = (CONST CHAR8*)AmlStreamGetCurrPos (FStream); + if (Buffer == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Parse the NameString information. + Status = AmlParseNameStringInfo ( + Buffer, + &Root, + &ParentPrefix, + &SegCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Get the beginning of the raw NameString. + RawPathBuffer = (UINT8*)AmlGetFirstNameSeg ( + Buffer, + Root, + ParentPrefix + ); + if (RawPathBuffer == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Initialize a backward stream containing the raw path. + Status = AmlStreamInit ( + RawPathNameBStream, + RawPathBuffer, + (SegCount * AML_NAME_SEG_SIZE), + EAmlStreamDirectionBackward + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Get the first node in the ParentNode branch that is part of the + AML namespace and has its name defined. + + This is different from getting the first namespace node. This function is + necessary because an absolute path is built while the tree is not complete + yet. The parsing is ongoing. + + For instance, the ASL statement "CreateXXXField ()" adds a field in the + AML namespace, but the name it defines is the last fixed argument of the + corresponding object. + If an AML path is referenced in its first fixed argument, it is not + possible to resolve the name of the CreateXXXField object. However, the AML + path is not part of the scope created by the CreateXXXField object, so this + scope can be skipped. + + In the following ASL code, the method invocation to MET0 is done in the + "CreateField" statement. The "CreateField" statement defines the "FIEL" + path in the AML namespace. However, MET0 must be not be resolved in the + "CreateField" object scope. It needs to be resolved in its parent. + ASL code: + Method (MET0, 0,,, BuffObj) { + Return (Buffer (0x1000) {}) + } + CreateField (MET0(), 0x100, 0x4, FIEL) + + @param [in] Node Node to get the first named node from, in + its hierarchy. + @param [out] OutNamedNode First named node in Node's hierarchy. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlGetFirstNamedAncestorNode ( + IN CONST AML_NODE_HEADER * Node, + OUT AML_NODE_HEADER ** OutNamedNode + ) +{ + EFI_STATUS Status; + CONST AML_NODE_HEADER * NameSpaceNode; + + if ((!IS_AML_OBJECT_NODE (Node) && + !IS_AML_ROOT_NODE (Node)) || + (OutNamedNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // If Node is not the root node and doesn't have a name defined yet, + // get the ancestor NameSpace node. + while (!IS_AML_ROOT_NODE (Node) && + !(AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)Node, + AML_IN_NAMESPACE) && + AmlNodeGetName ((CONST AML_OBJECT_NODE*)Node) != NULL)) { + Status = AmlGetFirstAncestorNameSpaceNode ( + Node, + (AML_NODE_HEADER**)&NameSpaceNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + // The NameSpaceNode may not have its name defined as yet. In this + // case get the next ancestor node. + Node = NameSpaceNode; + } + + *OutNamedNode = (AML_NODE_HEADER*)Node; + + return EFI_SUCCESS; +} + +/** From a ParentNode and a forward stream pointing to a relative path, + build a raw AML absolute path and return it in a backward stream. + + No memory is allocated in this function, the out stream must be initialized + with a buffer long enough to hold any raw absolute AML path. + + @param [in] ParentNode Parent node of the namespace + node from which the absolute + path is built. ParentNode isn't + necessarily a namespace node. + Must be a root or an object node. + @param [in] PathnameFStream Forward stream pointing to the + beginning of a pathname (any + NameString). + The stream must not be at its end. + @param [in, out] AbsolutePathBStream Backward stream where the raw + absolute path is written. The + stream must be already initialized. + The stream must not be at its end. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlBuildRawMethodAbsolutePath ( + IN CONST AML_NODE_HEADER * ParentNode, + IN CONST AML_STREAM * PathnameFStream, + IN OUT AML_STREAM * AbsolutePathBStream + ) +{ + EFI_STATUS Status; + + AML_NODE_HEADER * NamedParentNode; + UINT8 * RawPathBuffer; + CONST CHAR8 * CurrPos; + + UINT32 Root; + UINT32 ParentPrefix; + UINT32 SegCount; + + if ((!IS_AML_OBJECT_NODE (ParentNode) && + !IS_AML_ROOT_NODE (ParentNode)) || + !IS_STREAM (PathnameFStream) || + IS_END_OF_STREAM (PathnameFStream) || + !IS_STREAM_FORWARD (PathnameFStream) || + !IS_STREAM (AbsolutePathBStream) || + IS_END_OF_STREAM (AbsolutePathBStream) || + !IS_STREAM_BACKWARD (AbsolutePathBStream)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + CurrPos = (CONST CHAR8*)AmlStreamGetCurrPos (PathnameFStream); + if (CurrPos == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Parse the NameString information. + Status = AmlParseNameStringInfo ( + CurrPos, + &Root, + &ParentPrefix, + &SegCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Copy the method invocation raw relative path at the end of the Stream. + if (SegCount != 0) { + // Get the beginning of the raw NameString. + RawPathBuffer = (UINT8*)AmlGetFirstNameSeg ( + CurrPos, + Root, + ParentPrefix + ); + + Status = AmlStreamWrite ( + AbsolutePathBStream, + RawPathBuffer, + SegCount * AML_NAME_SEG_SIZE + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + // If the pathname contained an absolute path, this is finished, return. + if (Root) { + return Status; + } + + // Get the first named node of the parent node in its hierarchy. + Status = AmlGetFirstNamedAncestorNode (ParentNode, &NamedParentNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Build the raw absolute path of the namespace node. + Status = AmlGetRawNameSpacePath ( + NamedParentNode, + ParentPrefix, + AbsolutePathBStream + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Compare two raw NameStrings stored in forward streams. + Compare them NameSeg by NameSeg (a NameSeg is 4 bytes long). + + The two raw NameStrings can be of different size. + + @param [in] RawFStream1 First forward stream to compare. + Points to the beginning of the raw NameString. + @param [in] RawFStream2 Second forward stream to compare. + Points to the beginning of the raw NameString. + @param [out] CompareCount Count of identic bytes. + Must be a multiple of 4 (size of a NameSeg). + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlCompareRawNameString ( + IN CONST AML_STREAM * RawFStream1, + IN CONST AML_STREAM * RawFStream2, + OUT UINT32 * CompareCount + ) +{ + EFI_STATUS Status; + UINT32 Index; + + AML_STREAM RawFStream1Clone; + AML_STREAM RawFStream2Clone; + UINT32 Stream1Size; + UINT32 Stream2Size; + UINT32 CompareLen; + + // Raw NameStrings have a size that is a multiple of the size of NameSegs. + if (!IS_STREAM (RawFStream1) || + IS_END_OF_STREAM (RawFStream1) || + !IS_STREAM_FORWARD (RawFStream1) || + !IS_STREAM (RawFStream2) || + IS_END_OF_STREAM (RawFStream2) || + (CompareCount == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Stream1Size = AmlStreamGetFreeSpace (RawFStream1); + if ((Stream1Size & (AML_NAME_SEG_SIZE - 1)) != 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Stream2Size = AmlStreamGetFreeSpace (RawFStream2); + if ((Stream2Size & (AML_NAME_SEG_SIZE - 1)) != 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlStreamClone (RawFStream1, &RawFStream1Clone); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlStreamClone (RawFStream2, &RawFStream2Clone); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + CompareLen = MIN (Stream1Size, Stream2Size); + Index = 0; + // Check there is enough space for a NameSeg in both Stream1 and Stream2. + while (Index < CompareLen) { + if (!AmlStreamCmp ( + &RawFStream1Clone, + &RawFStream2Clone, + AML_NAME_SEG_SIZE) + ) { + // NameSegs are different. Break. + break; + } + + Status = AmlStreamProgress (&RawFStream1Clone, AML_NAME_SEG_SIZE); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + Status = AmlStreamProgress (&RawFStream2Clone, AML_NAME_SEG_SIZE); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Index += AML_NAME_SEG_SIZE; + } + + *CompareCount = Index; + + return EFI_SUCCESS; +} + +/** Check whether an alias can be resolved to a method definition. + + Indeed, the following ASL code must be handled: + Method (MET0, 1) { + Return (0x9) + } + Alias (\MET0, \ALI0) + Alias (\ALI0, \ALI1) + \ALI1(0x5) + When searching for \ALI1 in the AML NameSpace, it resolves to \ALI0. + When searching for \ALI0 in the AML NameSpace, it resolves to \MET0. + When searching for \MET0 in the AML NameSpace, it resolves to a method + definition. + + This method is a wrapper to recursively call AmlFindMethodDefinition. + + @param [in] AliasNode Pointer to an Alias object node. + @param [in] NameSpaceRefList List of NameSpaceRef nodes. + @param [out] OutNameSpaceRefNode If success, and if the alias is resolved + to a method definition (this can go + through other alias indirections), + containing the corresponding + NameSpaceRef node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlResolveAliasMethod ( + IN CONST AML_OBJECT_NODE * AliasNode, + IN CONST LIST_ENTRY * NameSpaceRefList, + OUT AML_NAMESPACE_REF_NODE ** OutNameSpaceRefNode + ) +{ + EFI_STATUS Status; + AML_STREAM SourceAliasFStream; + CONST AML_DATA_NODE * DataNode; + + if (!AmlNodeCompareOpCode (AliasNode, AML_ALIAS_OP, 0) || + (NameSpaceRefList == NULL) || + (OutNameSpaceRefNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // The aliased NameString (the source name) is the first fixed argument, + // cf. ACPI6.3 spec, s19.6.4: Alias (SourceObject, AliasObject) + DataNode = (CONST AML_DATA_NODE*)AmlGetFixedArgument ( + (AML_OBJECT_NODE*)AliasNode, + EAmlParseIndexTerm0 + ); + if (DataNode == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Initialize a stream on the source alias NameString. + Status = AmlStreamInit ( + &SourceAliasFStream, + DataNode->Buffer, + DataNode->Size, + EAmlStreamDirectionForward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Recursively check whether the source alias NameString + // is a method invocation. + Status = AmlIsMethodInvocation ( + AmlGetParent ((AML_NODE_HEADER*)AliasNode), + &SourceAliasFStream, + NameSpaceRefList, + OutNameSpaceRefNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + + return Status; +} + +/** Iterate through the MethodList to find the best namespace resolution. + If the pathname resolves to a method definition, returns it. + + For instance, if the AML namespace is: + \ + \-MET0 <- Device definition, absolute path: \MET0 + \-AAAA + \-MET0 <- Method definition, absolute path: \AAAA.MET0 + \-MET1 <- Method definition, absolute path: \AAAA.MET1 + \-BBBB + \-CCCC + \-DDDD + \-MET0 <- Method definition, absolute path: \AAAA.BBBB.CCCC.DDDD.MET0 + + The list of the available pathnames is: + [NameSpaceRefList] + - \MET0 <- Device definition + - \AAAA + - \AAAA.MET0 <- Method definition + - \AAAA.MET1 <- Method definition + - \AAAA.BBBB + - \AAAA.BBBB.CCCC + - \AAAA.BBBB.CCCC.DDDD + - \AAAA.BBBB.CCCC.DDDD.MET0 <- Method definition + + Depending on where the method invocation is done, the method definition + referenced changes. If the method call "MET0" is done from + \AAAA.BBBB.CCCC: + 1. Identify which pathnames end with "MET0": + - \MET0 <- Device definition + - \AAAA.MET0 <- Method definition + - \AAAA.BBBB.CCCC.DDDD.MET0 <- Method definition + 2. Resolve the method invocation: + - \AAAA.MET0 <- Method definition + 3. \AAAA.MET0 is a method definition, so return the corresponding + reference node. + + @param [in] RawAbsolutePathFStream Forward stream pointing to a raw + absolute path. + The stream must not be at its end. + @param [in] RawPathNameBStream Backward stream pointing to a raw + pathname. This raw pathname is the + raw NameString of namespace node. + The stream must not be at its end. + @param [in] NameSpaceRefList List of NameSpaceRef nodes. + @param [out] OutNameSpaceRefNode If the two input paths are + referencing a method definition, + returns the corresponding + NameSpaceRef node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlFindMethodDefinition ( + IN CONST AML_STREAM * RawAbsolutePathFStream, + IN CONST AML_STREAM * RawPathNameBStream, + IN CONST LIST_ENTRY * NameSpaceRefList, + OUT AML_NAMESPACE_REF_NODE ** OutNameSpaceRefNode + ) +{ + EFI_STATUS Status; + + LIST_ENTRY * NextLink; + + // To resolve a pathname, scope levels need to be compared. + UINT32 NameSegScopeCount; + UINT32 PathNameSegScopeCount; + UINT32 ProbedScopeCount; + UINT32 BestScopeCount; + + AML_STREAM ProbedRawAbsoluteFStream; + AML_STREAM ProbedRawAbsoluteBStream; + + AML_NAMESPACE_REF_NODE * ProbedNameSpaceRefNode; + AML_NAMESPACE_REF_NODE * BestNameSpaceRefNode; + + if (!IS_STREAM (RawAbsolutePathFStream) || + IS_END_OF_STREAM (RawAbsolutePathFStream) || + !IS_STREAM_FORWARD (RawAbsolutePathFStream) || + ((AmlStreamGetIndex (RawAbsolutePathFStream) & + (AML_NAME_SEG_SIZE - 1)) != 0) || + !IS_STREAM (RawPathNameBStream) || + IS_END_OF_STREAM (RawPathNameBStream) || + !IS_STREAM_BACKWARD (RawPathNameBStream) || + ((AmlStreamGetIndex (RawPathNameBStream) & + (AML_NAME_SEG_SIZE - 1)) != 0) || + (NameSpaceRefList == NULL) || + (OutNameSpaceRefNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + DEBUG ((DEBUG_VERBOSE, "AmlMethodParser: Checking absolute name: ")); + AmlDbgPrintChars ( + DEBUG_VERBOSE, + (CONST CHAR8*)AmlStreamGetCurrPos (RawAbsolutePathFStream), + AmlStreamGetMaxBufferSize (RawAbsolutePathFStream) + ); + DEBUG ((DEBUG_VERBOSE, ".\n")); + + BestNameSpaceRefNode = NULL; + BestScopeCount = 0; + NameSegScopeCount = AmlStreamGetMaxBufferSize (RawAbsolutePathFStream); + PathNameSegScopeCount = AmlStreamGetMaxBufferSize (RawPathNameBStream); + + // Iterate through the raw AML absolute path to find the best match. + DEBUG ((DEBUG_VERBOSE, "AmlMethodParser: Comparing with: ")); + NextLink = NameSpaceRefList->ForwardLink; + while (NextLink != NameSpaceRefList) { + ProbedNameSpaceRefNode = (AML_NAMESPACE_REF_NODE*)NextLink; + + // Print the raw absolute path of the probed node. + AmlDbgPrintChars ( + DEBUG_VERBOSE, + ProbedNameSpaceRefNode->RawAbsolutePath, + ProbedNameSpaceRefNode->RawAbsolutePathSize + ); + DEBUG ((DEBUG_VERBOSE, "; ")); + + // If the raw AML absolute path of the probed node is longer than the + // searched pathname, continue. + // E.g.: The method call \MET0 cannot resolve to a method defined at + // \AAAA.MET0. The method definition is out of scope. + if (PathNameSegScopeCount > ProbedNameSpaceRefNode->RawAbsolutePathSize) { + NextLink = NextLink->ForwardLink; + continue; + } + + // Initialize a backward stream for the probed node. + // This stream is used to compare the ending of the pathnames. + // E.g. if the method searched ends with "MET0", pathnames not ending with + // "MET0" should be skipped. + Status = AmlStreamInit ( + &ProbedRawAbsoluteBStream, + (UINT8*)ProbedNameSpaceRefNode->RawAbsolutePath, + ProbedNameSpaceRefNode->RawAbsolutePathSize, + EAmlStreamDirectionBackward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Compare the pathname endings. If they don't match, continue. + if (!AmlStreamCmp ( + RawPathNameBStream, + &ProbedRawAbsoluteBStream, + AmlStreamGetMaxBufferSize (RawPathNameBStream))) { + NextLink = NextLink->ForwardLink; + continue; + } + + // Initialize a forward stream for the probed node. + // This stream is used to count how many scope levels from the root + // are common with the probed node. The more there are, the better it is. + // E.g.: For the method invocation \AAAA.BBBB.MET0, if there are 2 + // pathnames ending with MET0: + // - \AAAA.MET0 has 1 NameSeg in common with \AAAA.BBBB.MET0 + // from the root (this is "AAAA"); + // - \MET0 has 0 NameSeg in common with \AAAA.BBBB.MET0 + // from the root; + // Thus, the best match is \AAAA.MET0. + Status = AmlStreamInit ( + &ProbedRawAbsoluteFStream, + (UINT8*)ProbedNameSpaceRefNode->RawAbsolutePath, + ProbedNameSpaceRefNode->RawAbsolutePathSize, + EAmlStreamDirectionForward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Count how many namespace levels are in common from the root. + Status = AmlCompareRawNameString ( + RawAbsolutePathFStream, + &ProbedRawAbsoluteFStream, + &ProbedScopeCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (ProbedScopeCount == NameSegScopeCount) { + // This is a perfect match. Exit the loop. + BestNameSpaceRefNode = ProbedNameSpaceRefNode; + break; + } else if (ProbedScopeCount > BestScopeCount) { + // The probed node has more scope levels in common than the + // last best match. Update the best match. + BestScopeCount = ProbedScopeCount; + BestNameSpaceRefNode = ProbedNameSpaceRefNode; + } else if (ProbedScopeCount == BestScopeCount) { + // The probed node has the same number of scope levels in + // common as the last best match. + if (ProbedScopeCount == 0) { + // There was not best match previously. Set it. + BestNameSpaceRefNode = ProbedNameSpaceRefNode; + } else { + // (ProbedScopeCount != 0) + // If there is an equivalent candidate, the best has the shortest + // absolute path. Indeed, a similar ProbedScopeCount and a longer + // path means the definition is out of the scope. + // E.g.: For the method invocation \AAAA.BBBB.MET0, if there are 2 + // pathnames ending with MET0: + // - \AAAA.MET0 has 1 NameSegs in common with \AAAA.BBBB.MET0 + // from the root (this is "AAAA"); + // - \AAAA.CCCC.MET0 has 1 NameSegs in common with + // \AAAA.BBBB.MET0 from the root (this is "AAAA"); + // As \AAAA.CCCC.MET0 is longer than \AAAA.MET0, it means that + // the pathname could have matched on more NameSegs, but it + // didn't because it is out of scope. + // Thus, the best match is \AAAA.MET0. + if (AmlStreamGetIndex (&ProbedRawAbsoluteFStream) < + BestNameSpaceRefNode->RawAbsolutePathSize) { + BestScopeCount = ProbedScopeCount; + BestNameSpaceRefNode = ProbedNameSpaceRefNode; + } else if (AmlStreamGetIndex (&ProbedRawAbsoluteFStream) == + BestNameSpaceRefNode->RawAbsolutePathSize) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } + } + + NextLink = NextLink->ForwardLink; + } + + DEBUG ((DEBUG_VERBOSE, "\n")); + + // Check whether the BestNameSpaceRefNode is a method definition. + if (BestNameSpaceRefNode != NULL) { + if (AmlIsMethodDefinitionNode (BestNameSpaceRefNode->NodeRef)) { + *OutNameSpaceRefNode = BestNameSpaceRefNode; + } else if (AmlNodeCompareOpCode ( + BestNameSpaceRefNode->NodeRef, + AML_ALIAS_OP, 0)) { + // The path matches an alias. Resolve the alias and check whether + // this is a method defintion. + Status = AmlResolveAliasMethod ( + BestNameSpaceRefNode->NodeRef, + NameSpaceRefList, + OutNameSpaceRefNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + } else { + // If no, return NULL, even if a matching pathname has been found. + *OutNameSpaceRefNode = NULL; + } + + return EFI_SUCCESS; +} + +/** Check whether a pathname is a method invocation. + + If there is a matching method definition, returns the corresponding + NameSpaceRef node. + + To do so, the NameSpaceRefList is keeping track of every namespace node + and its raw AML absolute path. + To check whether a pathname is a method invocation, a corresponding raw + absolute pathname is built. This raw absolute pathname is then compared + to the list of available pathnames. If a pathname defining a method + matches the scope of the input pathname, return. + + @param [in] ParentNode Parent node. Node to which the node to be + created will be attached. + @param [in] FStream Forward stream pointing to the NameString + to find. + @param [in] NameSpaceRefList List of NameSpaceRef nodes. + @param [out] OutNameSpaceRefNode If the NameString pointed by FStream is + a method invocation, OutNameSpaceRefNode + contains the NameSpaceRef corresponding + to the method definition. + NULL otherwise. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlIsMethodInvocation ( + IN CONST AML_NODE_HEADER * ParentNode, + IN CONST AML_STREAM * FStream, + IN CONST LIST_ENTRY * NameSpaceRefList, + OUT AML_NAMESPACE_REF_NODE ** OutNameSpaceRefNode + ) +{ + EFI_STATUS Status; + + AML_STREAM RawPathNameBStream; + AML_STREAM RawAbsolutePathFStream; + + AML_STREAM RawAbsolutePathBStream; + UINT8 * RawAbsolutePathBuffer; + UINT32 RawAbsolutePathBufferSize; + + AML_NAMESPACE_REF_NODE * NameSpaceRefNode; + + if ((!IS_AML_OBJECT_NODE (ParentNode) && + !IS_AML_ROOT_NODE (ParentNode)) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (NameSpaceRefList == NULL) || + (OutNameSpaceRefNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // There cannot be a method invocation in a field list. Return. + if (AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)ParentNode, + AML_HAS_FIELD_LIST)) { + *OutNameSpaceRefNode = NULL; + return EFI_SUCCESS; + } + + // Allocate memory for the raw absolute path. + RawAbsolutePathBufferSize = MAX_AML_NAMESTRING_SIZE; + RawAbsolutePathBuffer = AllocateZeroPool (RawAbsolutePathBufferSize); + if (RawAbsolutePathBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + // Initialize a backward stream to get the raw absolute path. + Status = AmlStreamInit ( + &RawAbsolutePathBStream, + RawAbsolutePathBuffer, + RawAbsolutePathBufferSize, + EAmlStreamDirectionBackward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Build the raw AML absolute path of the namespace node. + Status = AmlBuildRawMethodAbsolutePath ( + ParentNode, + FStream, + &RawAbsolutePathBStream + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // If this is the root path: it cannot be a method invocation. Just return. + if (AmlStreamGetIndex (&RawAbsolutePathBStream) == 0) { + DEBUG (( + DEBUG_VERBOSE, + "AmlMethodParser: " + "Root node cannot be a method invocation\n" + )); + *OutNameSpaceRefNode = NULL; + Status = EFI_SUCCESS; + goto exit_handler; + } + + // Create a forward stream for the raw absolute path. + // This forward stream only contains the raw absolute path with + // no extra free space. + Status = AmlStreamInit ( + &RawAbsolutePathFStream, + AmlStreamGetCurrPos (&RawAbsolutePathBStream), + AmlStreamGetIndex (&RawAbsolutePathBStream), + EAmlStreamDirectionForward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Create a backward stream for the node name. + Status = AmlInitRawPathBStream ( + FStream, + &RawPathNameBStream + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Go through the NameSpaceRefList elements to check for + // a corresponding method definition. + NameSpaceRefNode = NULL; + Status = AmlFindMethodDefinition ( + &RawAbsolutePathFStream, + &RawPathNameBStream, + NameSpaceRefList, + &NameSpaceRefNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + +#if !defined(MDEPKG_NDEBUG) + // Print whether a method definition has been found. + if (NameSpaceRefNode != NULL) { + DEBUG (( + DEBUG_VERBOSE, + "AmlMethodParser: Corresponding method definition: " + )); + AmlDbgPrintChars ( + DEBUG_VERBOSE, + NameSpaceRefNode->RawAbsolutePath, + NameSpaceRefNode->RawAbsolutePathSize + ); + DEBUG ((DEBUG_VERBOSE, ".\n")); + + } else { + DEBUG ((DEBUG_VERBOSE, "AmlMethodParser: No method definition found.\n")); + } +#endif // MDEPKG_NDEBUG + + *OutNameSpaceRefNode = NameSpaceRefNode; + +exit_handler: + // Free allocated memory. + FreePool (RawAbsolutePathBuffer); + return Status; +} + +/** Create a namespace reference node and add it to the NameSpaceRefList. + + When a namespace node is encountered, the namespace it defines must be + associated to the node. This allow to keep track of the nature of each + name present in the AML namespace. + + In the end, this allows to recognize method invocations and parse the right + number of arguments after the method name. + + @param [in] Node Namespace node. + @param [in, out] NameSpaceRefList List of namespace reference nodes. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlAddNameSpaceReference ( + IN CONST AML_OBJECT_NODE * Node, + IN OUT LIST_ENTRY * NameSpaceRefList + ) +{ + EFI_STATUS Status; + AML_NAMESPACE_REF_NODE * NameSpaceRefNode; + + AML_STREAM NodeNameFStream; + EAML_PARSE_INDEX NameIndex; + CONST AML_DATA_NODE * NameNode; + + AML_STREAM RawAbsolutePathBStream; + UINT32 RawAbsolutePathBStreamSize; + + CHAR8 * AbsolutePathBuffer; + UINT32 AbsolutePathBufferSize; + + CONST AML_NODE_HEADER * ParentNode; + + if (!AmlNodeHasAttribute (Node, AML_IN_NAMESPACE) || + (NameSpaceRefList == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Allocate a buffer to get the raw AML absolute pathname of the + // namespace node. + AbsolutePathBufferSize = MAX_AML_NAMESTRING_SIZE; + AbsolutePathBuffer = AllocateZeroPool (AbsolutePathBufferSize); + if (AbsolutePathBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + Status = AmlStreamInit ( + &RawAbsolutePathBStream, + (UINT8*)AbsolutePathBuffer, + AbsolutePathBufferSize, + EAmlStreamDirectionBackward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto exit_handler; + } + + // Get the index where the name of the Node is stored in its + // fixed list of arguments. + Status = AmlNodeGetNameIndex (Node, &NameIndex); + if (EFI_ERROR (Status)) { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto exit_handler; + } + + // Get the Node name. + NameNode = (CONST AML_DATA_NODE*)AmlGetFixedArgument ( + (AML_OBJECT_NODE*)Node, + NameIndex + ); + if (!IS_AML_DATA_NODE (NameNode) || + (NameNode->DataType != EAmlNodeDataTypeNameString)) { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto exit_handler; + } + + // Initialize a stream on the node name of the namespace node. + // This is an AML NameString. + Status = AmlStreamInit ( + &NodeNameFStream, + NameNode->Buffer, + NameNode->Size, + EAmlStreamDirectionForward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto exit_handler; + } + + ParentNode = AmlGetParent ((AML_NODE_HEADER*)Node); + if (ParentNode == NULL) { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto exit_handler; + } + + // Build the raw AML absolute path of the namespace node. + Status = AmlBuildRawMethodAbsolutePath ( + ParentNode, + &NodeNameFStream, + &RawAbsolutePathBStream + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + RawAbsolutePathBStreamSize = AmlStreamGetIndex (&RawAbsolutePathBStream); + // This is the root path: this cannot be a method invocation. + if (RawAbsolutePathBStreamSize == 0) { + Status = EFI_SUCCESS; + goto exit_handler; + } + + // Create a NameSpace reference node. + Status = AmlCreateMethodRefNode ( + Node, + (CONST CHAR8*)AmlStreamGetCurrPos (&RawAbsolutePathBStream), + RawAbsolutePathBStreamSize, + &NameSpaceRefNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Add the created NameSpaceRefNode to the list. + InsertTailList (NameSpaceRefList, &NameSpaceRefNode->Link); + + DEBUG (( + DEBUG_VERBOSE, + "AmlMethodParser: Adding namespace reference with name:\n" + )); + AmlDbgPrintChars ( + DEBUG_VERBOSE, + (CONST CHAR8*)AmlStreamGetCurrPos (&RawAbsolutePathBStream), + AmlStreamGetIndex (&RawAbsolutePathBStream) + ); + DEBUG ((DEBUG_VERBOSE, "\n")); + +exit_handler: + // Free allocated memory. + FreePool (AbsolutePathBuffer); + + return Status; +} + +/** Create a method invocation node. + + The AML grammar does not attribute an OpCode/SubOpCode couple for + method invocations. This library is representing method invocations + as if they had one. + + The AML encoding for method invocations in the ACPI specification 6.3 is: + MethodInvocation := NameString TermArgList + In this library, it is: + MethodInvocation := MethodInvocationOp NameString ArgumentCount TermArgList + ArgumentCount := ByteData + + When computing the size of a tree or serializing it, the additional data is + not taken into account (i.e. the MethodInvocationOp and the ArgumentCount). + + Method invocation nodes have the AML_METHOD_INVOVATION attribute. + + @param [in] NameSpaceRefNode NameSpaceRef node pointing to the + the definition of the invoked + method. + @param [in] MethodInvocationName Data node containing the method + invocation name. + @param [out] MethodInvocationNodePtr Created method invocation node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCreateMethodInvocationNode ( + IN CONST AML_NAMESPACE_REF_NODE * NameSpaceRefNode, + IN AML_DATA_NODE * MethodInvocationName, + OUT AML_OBJECT_NODE ** MethodInvocationNodePtr + ) +{ + EFI_STATUS Status; + + UINT8 ArgCount; + AML_DATA_NODE * ArgCountNode; + AML_NODE_HEADER ** FixedArgs; + AML_OBJECT_NODE * MethodDefinitionNode; + AML_OBJECT_NODE * MethodInvocationNode; + + if ((NameSpaceRefNode == NULL) || + !AmlIsMethodDefinitionNode (NameSpaceRefNode->NodeRef) || + !IS_AML_DATA_NODE (MethodInvocationName) || + (MethodInvocationName->DataType != EAmlNodeDataTypeNameString) || + (MethodInvocationNodePtr == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the number of arguments of the method. + MethodDefinitionNode = (AML_OBJECT_NODE*)NameSpaceRefNode->NodeRef; + FixedArgs = MethodDefinitionNode->FixedArgs; + // The method definition is an actual method definition. + if (AmlNodeCompareOpCode (MethodDefinitionNode, AML_METHOD_OP, 0)) { + // Cf ACPI 6.3 specification: + // DefMethod := MethodOp PkgLength NameString MethodFlags TermList + // MethodOp := 0x14 + // MethodFlags := ByteData bit 0-2: ArgCount (0-7) + // bit 3: SerializeFlag + // 0 NotSerialized + // 1 Serialized + // bit 4-7: SyncLevel (0x00-0x0f) + + // Read the MethodFlags to decode the ArgCount. + ArgCountNode = (AML_DATA_NODE*)FixedArgs[EAmlParseIndexTerm1]; + ArgCount = *((UINT8*)ArgCountNode->Buffer) & 0x7; + } else if (AmlNodeCompareOpCode (MethodDefinitionNode, AML_EXTERNAL_OP, 0)) { + // The method definition is an external statement. + // Cf ACPI 6.3 specification: + // DefExternal := ExternalOp NameString ObjectType ArgumentCount + // ExternalOp := 0x15 + // ObjectType := ByteData + // ArgumentCount := ByteData (0 – 7) + + // Read the ArgumentCount. + ArgCountNode = (AML_DATA_NODE*)FixedArgs[EAmlParseIndexTerm2]; + ArgCount = *((UINT8*)ArgCountNode->Buffer); + } else { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Create the object node for the method invocation. + // MethodInvocation := MethodInvocationOp NameString ArgumentCount + // MethodInvocationOp := Pseudo Opcode for Method Invocation + // NameString := Method Name + // ArgumentCount := ByteData (0 – 7) + Status = AmlCreateObjectNode ( + AmlGetByteEncodingByOpCode (AML_METHOD_INVOC_OP, 0), + 0, + &MethodInvocationNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // The first fixed argument is the method name. + Status = AmlSetFixedArgument ( + MethodInvocationNode, + EAmlParseIndexTerm0, + (AML_NODE_HEADER*)MethodInvocationName + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + // Create a data node holding the number of arguments + // of the method invocation. + ArgCountNode = NULL; + Status = AmlCreateDataNode ( + EAmlNodeDataTypeUInt, + &ArgCount, + sizeof (UINT8), + &ArgCountNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + // The second fixed argument is the number of arguments. + Status = AmlSetFixedArgument ( + MethodInvocationNode, + EAmlParseIndexTerm1, + (AML_NODE_HEADER*)ArgCountNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + *MethodInvocationNodePtr = MethodInvocationNode; + return Status; + +error_handler: + // Delete the sub-tree: the method invocation name is already attached. + AmlDeleteTree ((AML_NODE_HEADER*)MethodInvocationNode); + if (ArgCountNode != NULL) { + AmlDeleteNode ((AML_NODE_HEADER*)ArgCountNode); + } + + return Status; +} + +/** Get the number of arguments of a method invocation node. + + This function also allow to identify whether a node is a method invocation + node. If the input node is not a method invocation node, just return. + + @param [in] MethodInvocationNode Method invocation node. + @param [out] IsMethodInvocation Boolean stating whether the input + node is a method invocation. + @param [out] ArgCount Number of arguments of the method + invocation. + Set to 0 if MethodInvocationNode + is not a method invocation. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +*/ +EFI_STATUS +EFIAPI +AmlGetMethodInvocationArgCount ( + IN CONST AML_OBJECT_NODE * MethodInvocationNode, + OUT BOOLEAN * IsMethodInvocation, + OUT UINT8 * ArgCount + ) +{ + AML_DATA_NODE * NumArgsNode; + + if (!IS_AML_NODE_VALID (MethodInvocationNode) || + (IsMethodInvocation == NULL) || + (ArgCount == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Check whether MethodInvocationNode is a method invocation. + if (!AmlNodeCompareOpCode (MethodInvocationNode, AML_METHOD_INVOC_OP, 0)) { + *IsMethodInvocation = FALSE; + *ArgCount = 0; + return EFI_SUCCESS; + } + + // MethodInvocation := MethodInvocationOp NameString ArgumentCount + // MethodInvocationOp := Pseudo Opcode for Method Invocation + // NameString := Method Name + // ArgumentCount := ByteData (0 - 7) + NumArgsNode = (AML_DATA_NODE*)AmlGetFixedArgument ( + (AML_OBJECT_NODE*)MethodInvocationNode, + EAmlParseIndexTerm1 + ); + if (!IS_AML_NODE_VALID (NumArgsNode) || + (NumArgsNode->Buffer == NULL) || + (NumArgsNode->DataType != EAmlNodeDataTypeUInt) || + (NumArgsNode->Size != 1)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + *ArgCount = *NumArgsNode->Buffer; + + *IsMethodInvocation = TRUE; + return EFI_SUCCESS; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h new file mode 100644 index 000000000..b082f2deb --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlMethodParser.h @@ -0,0 +1,188 @@ +/** @file + AML Method Parser. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_METHOD_PARSER_H_ +#define AML_METHOD_PARSER_H_ + +#include +#include + +/** AML namespace reference node. + + Namespace reference nodes allow to associate an AML absolute pathname + to the tree node defining this object in the namespace. + + Namespace reference nodes are stored in a separate list. They are not part of + the tree. +*/ +typedef struct AmlNameSpaceRefNode { + /// Double linked list. + /// This must be the first field in this structure. + LIST_ENTRY Link; + + /// Node part of the AML namespace. It must have the AML_IN_NAMESPACE + /// attribute. + CONST AML_OBJECT_NODE * NodeRef; + + /// Raw AML absolute pathname of the NodeRef. + /// This is a raw AML NameString (cf AmlNameSpace.c: A concatenated list + /// of 4 chars long names. The dual/multi NameString prefix have been + /// stripped.). + CONST CHAR8 * RawAbsolutePath; + + /// Size of the raw AML absolute pathname buffer. + UINT32 RawAbsolutePathSize; +} AML_NAMESPACE_REF_NODE; + +/** Delete a list of namespace reference nodes. + + @param [in] NameSpaceRefList List of namespace reference nodes. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeleteNameSpaceRefList ( + IN LIST_ENTRY * NameSpaceRefList + ); + + +#if !defined (MDEPKG_NDEBUG) +/** Print the list of raw absolute paths of the NameSpace reference list. + + @param [in] NameSpaceRefList List of NameSpace reference nodes. +**/ +VOID +EFIAPI +AmlDbgPrintNameSpaceRefList ( + IN CONST LIST_ENTRY * NameSpaceRefList + ); + +#endif // MDEPKG_NDEBUG + +/** Check whether a pathname is a method invocation. + + If there is a matching method definition, returns the corresponding + NameSpaceRef node. + + To do so, the NameSpaceRefList is keeping track of every namespace node + and its raw AML absolute path. + To check whether a pathname is a method invocation, a corresponding raw + absolute pathname is built. This raw absolute pathname is then compared + to the list of available pathnames. If a pathname defining a method + matches the scope of the input pathname, return. + + @param [in] ParentNode Parent node. Node to which the node to be + created will be attached. + @param [in] FStream Forward stream pointing to the NameString + to find. + @param [in] NameSpaceRefList List of NameSpaceRef nodes. + @param [out] OutNameSpaceRefNode If the NameString pointed by FStream is + a method invocation, OutNameSpaceRefNode + contains the NameSpaceRef corresponding + to the method definition. + NULL otherwise. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlIsMethodInvocation ( + IN CONST AML_NODE_HEADER * ParentNode, + IN CONST AML_STREAM * FStream, + IN CONST LIST_ENTRY * NameSpaceRefList, + OUT AML_NAMESPACE_REF_NODE ** OutNameSpaceRefNode + ); + +/** Create a namespace reference node and add it to the NameSpaceRefList. + + When a namespace node is encountered, the namespace it defines must be + associated to the node. This allow to keep track of the nature of each + name present in the AML namespace. + + In the end, this allows to recognize method invocations and parse the right + number of arguments after the method name. + + @param [in] Node Namespace node. + @param [in, out] NameSpaceRefList List of namespace reference nodes. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlAddNameSpaceReference ( + IN CONST AML_OBJECT_NODE * Node, + IN OUT LIST_ENTRY * NameSpaceRefList + ); + +/** Create a method invocation node. + + The AML grammar does not attribute an OpCode/SubOpCode couple for + method invocations. This library is representing method invocations + as if they had one. + + The AML encoding for method invocations in the ACPI specification 6.3 is: + MethodInvocation := NameString TermArgList + In this library, it is: + MethodInvocation := MethodInvocationOp NameString ArgumentCount TermArgList + ArgumentCount := ByteData + + When computing the size of a tree or serializing it, the additional data is + not taken into account (i.e. the MethodInvocationOp and the ArgumentCount). + + Method invocation nodes have the AML_METHOD_INVOVATION attribute. + + @param [in] NameSpaceRefNode NameSpaceRef node pointing to the + the definition of the invoked + method. + @param [in] MethodInvocationName Data node containing the method + invocation name. + @param [out] MethodInvocationNodePtr Created method invocation node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCreateMethodInvocationNode ( + IN CONST AML_NAMESPACE_REF_NODE * NameSpaceRefNode, + IN AML_DATA_NODE * MethodInvocationName, + OUT AML_OBJECT_NODE ** MethodInvocationNodePtr + ); + +/** Get the number of arguments of a method invocation node. + + This function also allow to identify whether a node is a method invocation + node. If the input node is not a method invocation node, just return. + + @param [in] MethodInvocationNode Method invocation node. + @param [out] IsMethodInvocation Boolean stating whether the input + node is a method invocation. + @param [out] ArgCount Number of arguments of the method + invocation. + Set to 0 if MethodInvocationNode + is not a method invocation. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +*/ +EFI_STATUS +EFIAPI +AmlGetMethodInvocationArgCount ( + IN CONST AML_OBJECT_NODE * MethodInvocationNode, + OUT BOOLEAN * IsMethodInvocation, + OUT UINT8 * ArgCount + ); + +#endif // AML_METHOD_PARSER_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.c new file mode 100644 index 000000000..380ac9bbc --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.c @@ -0,0 +1,1448 @@ +/** @file + AML Parser. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + AML Tree + -------- + + Each ASL Statement is represented in AML as and ObjectNode. + Each ObjectNode has an Opcode and has up to six FixedArguments + followed by a list of VariableArguments. + (ObjectNode) + \ + |- [0][1][2][3][4][5] # Fixed Arguments + |- {(VarArg1)->(VarArg2)->(VarArg3)->...N} # Variable Arguments + + A RootNode is a special type of Object Node that does not have an + Opcode or Fixed Arguments. It only has a list of VariableArguments + (RootNode) + \ + |- {(VarArg1)->(VarArg2)->(VarArg3)->...N} # Variable Arguments + + A DataNode consists of a data buffer. + + A FixedArgument or VariableArgument can be either an ObjectNode or + a DataNode. + + Example: + ASL code sample: + Device (DEV0) { + Name (VAR0, 0x6) + } + + Tree generated from the ASL code: + (RootNode) + \ + |- {(Device statement (ObjectNode))} # Variable Arg of the + \ # RootNode + | + |- [0] - Device Name (DataNode)(="DEV0") # Fixed Arg0 of the + | # Device() statement + | + |- {(Name statement (ObjectNode))} # Variable Arg of the + \ # Device() statement + | + |- [0] - Name statement(DataNode)(="VAR0") # Fixed Arg0 of the + | # Name() statement + |- [1] - Value(DataNode)(=0x6) # Fixed Arg1 of the + # Name() statement +*/ + +// Forward declaration. +STATIC +EFI_STATUS +EFIAPI +AmlParseStream ( + IN AML_NODE_HEADER * Node, + IN OUT AML_STREAM * FStream, + IN OUT LIST_ENTRY * NameSpaceRefList + ); + +/** Function pointer to parse an AML construct. + + The expected format of the AML construct is passed in the + ExpectedFormat argument. The available formats are available in + the AML_PARSE_FORMAT enum definition. + + An object node or a data node is created in the function, + and returned through the OutNode parameter. This node should + be attached after this function returns. + + @param [in] ParentNode Parent node to which the parsed + AML construct will be attached. + @param [in] ExpectedFormat Format of the AML construct to parse. + @param [in, out] FStream Forward stream containing the AML bytecode + to parse. + The stream must not be at its end. + @param [out] OutNode Pointer holding the node created from the + parsed AML bytecode. + + @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. +**/ +typedef +EFI_STATUS +EFIAPI +(*AML_PARSE_FUNCTION) ( + IN CONST AML_NODE_HEADER * Node, + IN AML_PARSE_FORMAT ExpectedFormat, + IN OUT AML_STREAM * FStream, + OUT AML_NODE_HEADER ** OutNode + ); + +/** Parse a UInt (where X=8, 16, 32 or 64). + + A data node is created and returned through the OutNode parameter. + + @param [in] ParentNode Parent node to which the parsed + AML construct will be attached. + @param [in] ExpectedFormat Format of the AML construct to parse. + @param [in, out] FStream Forward stream containing the AML bytecode + to parse. + The stream must not be at its end. + @param [out] OutNode Pointer holding the node created from the + parsed AML bytecode. + + @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. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlParseUIntX ( + IN CONST AML_NODE_HEADER * ParentNode, + IN AML_PARSE_FORMAT ExpectedFormat, + IN OUT AML_STREAM * FStream, + OUT AML_NODE_HEADER ** OutNode + ) +{ + EFI_STATUS Status; + UINT32 UIntXSize; + + if ((!IS_AML_ROOT_NODE (ParentNode) && + !IS_AML_OBJECT_NODE (ParentNode)) || + ((ExpectedFormat != EAmlUInt8) && + (ExpectedFormat != EAmlUInt16) && + (ExpectedFormat != EAmlUInt32) && + (ExpectedFormat != EAmlUInt64)) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (OutNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + switch (ExpectedFormat) { + case EAmlUInt8: + UIntXSize = 1; + break; + case EAmlUInt16: + UIntXSize = 2; + break; + case EAmlUInt32: + UIntXSize = 4; + break; + case EAmlUInt64: + UIntXSize = 8; + break; + default: + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlCreateDataNode ( + AmlTypeToNodeDataType (ExpectedFormat), + AmlStreamGetCurrPos (FStream), + UIntXSize, + (AML_DATA_NODE**)OutNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + DumpRaw (AmlStreamGetCurrPos (FStream), UIntXSize); + + // Move stream forward by the size of UIntX. + Status = AmlStreamProgress (FStream, UIntXSize); + if (EFI_ERROR (Status)) { + AmlDeleteTree (*OutNode); + ASSERT (0); + } + + return Status; +} + +/** Parse an AML NameString. + + A data node is created and returned through the OutNode parameter. + + @param [in] ParentNode Parent node to which the parsed + AML construct will be attached. + @param [in] ExpectedFormat Format of the AML construct to parse. + @param [in, out] FStream Forward stream containing the AML bytecode + to parse. + The stream must not be at its end. + @param [out] OutNode Pointer holding the node created from the + parsed AML bytecode. + + @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. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlParseNameString ( + IN CONST AML_NODE_HEADER * ParentNode, + IN AML_PARSE_FORMAT ExpectedFormat, + IN OUT AML_STREAM * FStream, + OUT AML_NODE_HEADER ** OutNode + ) +{ + EFI_STATUS Status; + + CONST UINT8 * Buffer; + CONST AML_BYTE_ENCODING * ByteEncoding; + UINT32 StrSize; + + if ((!IS_AML_ROOT_NODE (ParentNode) && + !IS_AML_OBJECT_NODE (ParentNode)) || + (ExpectedFormat != EAmlName) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (OutNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Buffer = (CONST UINT8*)AmlStreamGetCurrPos (FStream); + ByteEncoding = AmlGetByteEncoding (Buffer); + if ((ByteEncoding == NULL) || + ((ByteEncoding->Attribute & AML_IS_NAME_CHAR) == 0)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Parse the NameString. + Status = AmlGetNameStringSize ((CONST CHAR8*)Buffer, &StrSize); + if ((EFI_ERROR (Status)) || + (StrSize > AmlStreamGetFreeSpace (FStream))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlCreateDataNode ( + EAmlNodeDataTypeNameString, + Buffer, + StrSize, + (AML_DATA_NODE**)OutNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + DumpRaw (AmlStreamGetCurrPos (FStream), StrSize); + + // Move the stream forward by StrSize. + Status = AmlStreamProgress (FStream, StrSize); + if (EFI_ERROR (Status)) { + AmlDeleteTree (*OutNode); + ASSERT (0); + } + + return Status; +} + +/** Parse an AML String. + + A data node is created and returned through the OutNode parameter. + + @param [in] ParentNode Parent node to which the parsed + AML construct will be attached. + @param [in] ExpectedFormat Format of the AML construct to parse. + @param [in, out] FStream Forward stream containing the AML bytecode + to parse. + The stream must not be at its end. + @param [out] OutNode Pointer holding the node created from the + parsed AML bytecode. + + @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. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlParseString ( + IN CONST AML_NODE_HEADER * ParentNode, + IN AML_PARSE_FORMAT ExpectedFormat, + IN OUT AML_STREAM * FStream, + OUT AML_NODE_HEADER ** OutNode + ) +{ + EFI_STATUS Status; + UINT32 StrSize; + UINT8 Byte; + CONST UINT8 * Buffer; + + if ((!IS_AML_ROOT_NODE (ParentNode) && + !IS_AML_OBJECT_NODE (ParentNode)) || + (ExpectedFormat != EAmlString) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (OutNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Buffer = (CONST UINT8*)AmlStreamGetCurrPos (FStream); + StrSize = 0; + // AML String is NULL terminated. + do { + // Reading the stream moves the stream forward aswell. + Status = AmlStreamReadByte (FStream, &Byte); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + StrSize++; + } while (Byte != '\0'); + + DumpRaw (Buffer, StrSize); + + Status = AmlCreateDataNode ( + AmlTypeToNodeDataType (ExpectedFormat), + Buffer, + StrSize, + (AML_DATA_NODE**)OutNode + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Parse an AML object. + + An object can be resolved as an AML object with an OpCode, + or a NameString. An object node or a data node is created + and returned through the OutNode parameter. + + @param [in] ParentNode Parent node to which the parsed + AML construct will be attached. + @param [in] ExpectedFormat Format of the AML construct to parse. + @param [in, out] FStream Forward stream containing the AML bytecode + to parse. + The stream must not be at its end. + @param [out] OutNode Pointer holding the node created from the + parsed AML bytecode. + + @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. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlParseObject ( + IN CONST AML_NODE_HEADER * ParentNode, + IN AML_PARSE_FORMAT ExpectedFormat, + IN OUT AML_STREAM * FStream, + OUT AML_NODE_HEADER ** OutNode + ) +{ + EFI_STATUS Status; + + UINT8 OpCodeSize; + UINT32 PkgLength; + UINT32 PkgOffset; + UINT32 FreeSpace; + + CONST AML_BYTE_ENCODING * AmlByteEncoding; + CONST UINT8 * Buffer; + + if ((!IS_AML_ROOT_NODE (ParentNode) && + !IS_AML_OBJECT_NODE (ParentNode)) || + (ExpectedFormat != EAmlObject) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (OutNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + PkgLength = 0; + + // 0. Get the AML Byte encoding. + AmlByteEncoding = AmlGetByteEncoding (AmlStreamGetCurrPos (FStream)); + if (AmlByteEncoding == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // 1. Check for NameString. + // Indeed a NameString can be found when an AML object is expected. + // e.g. VAR0 = 3 // VAR0 is assigned an object which is a UINT. + // VAR1 = VAR2 // VAR2 is a NameString. + // If this is a NameString, return. A NameString can be a variable, a + // method invocation, etc. + if ((AmlByteEncoding->Attribute & AML_IS_NAME_CHAR) != 0) { + Status = AmlParseNameString ( + ParentNode, + EAmlName, + FStream, + OutNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + return Status; + } + + // 2. Determine the OpCode size to move the stream forward. + Buffer = (CONST UINT8*)AmlStreamGetCurrPos (FStream); + if (*Buffer == AML_EXT_OP) { + OpCodeSize = 2; + } else { + OpCodeSize = 1; + } + Status = AmlStreamProgress (FStream, OpCodeSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Print the opcode. + DumpRaw (Buffer, OpCodeSize); + + if (!IS_END_OF_STREAM (FStream)) { + // 3. Parse the PkgLength field, if present. + if ((AmlByteEncoding->Attribute & AML_HAS_PKG_LENGTH) != 0) { + Buffer = (CONST UINT8*)AmlStreamGetCurrPos (FStream); + PkgOffset = AmlGetPkgLength (Buffer, &PkgLength); + if (PkgOffset == 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Print the package length. + DumpRaw (Buffer, PkgOffset); + + // Adjust the size of the stream if it is valid package length. + FreeSpace = AmlStreamGetFreeSpace (FStream); + if (FreeSpace > PkgLength) { + // Reduce the stream size by (FreeSpace - PkgLength) bytes. + AmlStreamReduceMaxBufferSize (FStream, FreeSpace - PkgLength); + } else if (FreeSpace != PkgLength) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlStreamProgress (FStream, PkgOffset); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + } else if ((AmlByteEncoding->Attribute & AML_HAS_PKG_LENGTH) != 0) { + // The stream terminated unexpectedly. A PkgLen had to be parsed. + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // 4. Create an Object Node. + Status = AmlCreateObjectNode ( + AmlByteEncoding, + PkgLength, + (AML_OBJECT_NODE**)OutNode + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Parse a FieldPkgLen. + + A FieldPkgLen can only be found in a field list, i.e. in a NamedField field + element. The PkgLen is otherwise part of the object node structure. + A data node is created and returned through the OutNode parameter. + + @param [in] ParentNode Parent node to which the parsed + AML construct will be attached. + @param [in] ExpectedFormat Format of the AML construct to parse. + @param [in, out] FStream Forward stream containing the AML bytecode + to parse. + The stream must not be at its end. + @param [out] OutNode Pointer holding the node created from the + parsed AML bytecode. + + @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. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlParseFieldPkgLen ( + IN CONST AML_NODE_HEADER * ParentNode, + IN AML_PARSE_FORMAT ExpectedFormat, + IN OUT AML_STREAM * FStream, + OUT AML_NODE_HEADER ** OutNode + ) +{ + EFI_STATUS Status; + EFI_STATUS Status1; + CONST UINT8 * Buffer; + UINT32 PkgOffset; + UINT32 PkgLength; + + if (!AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)ParentNode, + AML_IS_FIELD_ELEMENT + ) || + (ExpectedFormat != EAmlFieldPkgLen) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (OutNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Buffer = (CONST UINT8*)AmlStreamGetCurrPos (FStream); + + PkgOffset = AmlGetPkgLength (Buffer, &PkgLength); + if (PkgOffset == 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Warning: Since, updating of field elements is not supported, store the + // FieldPkgLength in a Data Node as a raw buffer. + Status = AmlCreateDataNode ( + AmlTypeToNodeDataType (ExpectedFormat), + Buffer, + PkgOffset, + (AML_DATA_NODE**)OutNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + DumpRaw (Buffer, PkgOffset); + + Status = AmlStreamProgress (FStream, PkgOffset); + if (EFI_ERROR (Status)) { + Status1 = AmlDeleteNode (*OutNode); + ASSERT_EFI_ERROR (Status1); + ASSERT (0); + } + + return Status; +} + +/** Array of functions pointers to parse the AML constructs. + + The AML Byte encoding tables in Aml.c describe the format of the AML + statements. The AML_PARSE_FORMAT enum definition lists these constructs + and the corresponding parsing functions. +*/ +AML_PARSE_FUNCTION mParseType[EAmlParseFormatMax] = { + NULL, // EAmlNone + AmlParseUIntX, // EAmlUInt8 + AmlParseUIntX, // EAmlUInt16 + AmlParseUIntX, // EAmlUInt32 + AmlParseUIntX, // EAmlUInt64 + AmlParseObject, // EAmlObject + AmlParseNameString, // EAmlName + AmlParseString, // EAmlString + AmlParseFieldPkgLen // EAmlFieldPkgLen +}; + +/** Check whether the NameString stored in the data node is a method invocation. + If so, create a method invocation node and return it. + + @param [in] ParentNode Node to which the parsed AML construct + will be attached. + @param [in] DataNode Data node containing a NameString, + potentially being a method invocation. + @param [in, out] NameSpaceRefList List of namespace reference nodes. + @param [out] OutNode Pointer holding the method invocation + node if the NameString contained in the + data node is a method invocation. + NULL otherwise. + + @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. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlCheckAndParseMethodInvoc ( + IN CONST AML_NODE_HEADER * ParentNode, + IN AML_DATA_NODE * DataNode, + IN OUT LIST_ENTRY * NameSpaceRefList, + OUT AML_OBJECT_NODE ** OutNode + ) +{ + EFI_STATUS Status; + AML_NAMESPACE_REF_NODE * NameSpaceRefNode; + AML_OBJECT_NODE * MethodInvocationNode; + AML_STREAM FStream; + + if ((!IS_AML_ROOT_NODE (ParentNode) && + !IS_AML_OBJECT_NODE (ParentNode)) || + !IS_AML_DATA_NODE (DataNode) || + (DataNode->DataType != EAmlNodeDataTypeNameString) || + (NameSpaceRefList == NULL) || + (OutNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Initialize a stream containing the NameString which is checked. + Status = AmlStreamInit ( + &FStream, + DataNode->Buffer, + DataNode->Size, + EAmlStreamDirectionForward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Check whether the NameString is a method invocation. + NameSpaceRefNode = NULL; + Status = AmlIsMethodInvocation ( + ParentNode, + &FStream, + NameSpaceRefList, + &NameSpaceRefNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + MethodInvocationNode = NULL; + if (NameSpaceRefNode != NULL) { + // A matching method definition has been found. + // Create a method invocation node. + Status = AmlCreateMethodInvocationNode ( + NameSpaceRefNode, + (AML_DATA_NODE*)DataNode, + &MethodInvocationNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + *OutNode = MethodInvocationNode; + + return EFI_SUCCESS; +} + +/** Call the appropriate function to parse the AML construct in the stream. + + The ExpectedFormat parameter allows to choose the right parsing function. + An object node or a data node is created according to format. + + @param [in] ParentNode Node to which the parsed AML construct + will be attached. + @param [in] ExpectedFormat Format of the AML construct to parse. + @param [in, out] FStream Forward stream containing the AML + bytecode to parse. + The stream must not be at its end. + @param [in, out] NameSpaceRefList List of namespace reference nodes. + @param [out] OutNode Pointer holding the node created from the + parsed AML bytecode. + + @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. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlParseArgument ( + IN CONST AML_NODE_HEADER * ParentNode, + IN AML_PARSE_FORMAT ExpectedFormat, + IN OUT AML_STREAM * FStream, + IN OUT LIST_ENTRY * NameSpaceRefList, + OUT AML_NODE_HEADER ** OutNode + ) +{ + EFI_STATUS Status; + AML_PARSE_FUNCTION ParsingFunction; + AML_DATA_NODE * DataNode; + AML_OBJECT_NODE * MethodInvocationNode; + + if ((!IS_AML_ROOT_NODE (ParentNode) && + !IS_AML_OBJECT_NODE (ParentNode)) || + (ExpectedFormat >= EAmlParseFormatMax) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (NameSpaceRefList == NULL) || + (OutNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ParsingFunction = mParseType[ExpectedFormat]; + if (ParsingFunction == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Note: The ParsingFunction moves the stream forward as it + // consumes the AML bytecode + Status = ParsingFunction ( + ParentNode, + ExpectedFormat, + FStream, + OutNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Check whether the parsed argument is a NameString when an object + // is expected. In such case, it could be a method invocation. + DataNode = (AML_DATA_NODE*)*OutNode; + if (IS_AML_DATA_NODE (DataNode) && + (DataNode->DataType == EAmlNodeDataTypeNameString) && + (ExpectedFormat == EAmlObject)) { + Status = AmlCheckAndParseMethodInvoc ( + ParentNode, + (AML_DATA_NODE*)*OutNode, + NameSpaceRefList, + &MethodInvocationNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // A method invocation node has been created and the DataNode containing + // the NameString has been attached to the MethodInvocationNode. + // Replace the OutNode with the MethodInvocationNode. + if (MethodInvocationNode != NULL) { + *OutNode = (AML_NODE_HEADER*)MethodInvocationNode; + } + } + + return Status; +} + +/** Parse the Bytelist in the stream. + According to the content of the stream, create data node(s) + and add them to the variable list of arguments. + The byte list may be a list of resource data element or a simple byte list. + + @param [in] BufferNode Object node having a byte list. + @param [in, out] FStream Forward stream containing the AML bytecode + to parse. + The stream must not be at its end. + + @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 +AmlParseByteList ( + IN AML_OBJECT_NODE * BufferNode, + IN OUT AML_STREAM * FStream + ) +{ + EFI_STATUS Status; + AML_NODE_HEADER * NewNode; + CONST UINT8 * Buffer; + UINT32 BufferSize; + + // Check whether the node is an Object Node and has byte list. + if (!AmlNodeHasAttribute (BufferNode, AML_HAS_BYTE_LIST) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // The buffer contains a list of resource data elements. + if (AmlRdIsResourceDataBuffer (FStream)) { + // Parse the resource data elements and add them as data nodes. + // AmlParseResourceData() moves the stream forward. + Status = AmlParseResourceData (BufferNode, FStream); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + } else { + // The buffer doesn't contain a list of resource data elements. + // Create a single node holding the whole buffer data. + + // CreateDataNode checks the Buffer and BufferSize values. + Buffer = (CONST UINT8*)AmlStreamGetCurrPos (FStream); + BufferSize = AmlStreamGetFreeSpace (FStream); + + Status = AmlCreateDataNode ( + EAmlNodeDataTypeRaw, + Buffer, + BufferSize, + (AML_DATA_NODE**)&NewNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlVarListAddTailInternal ( + (AML_NODE_HEADER*)BufferNode, + NewNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + AmlDeleteTree (NewNode); + return Status; + } + + DumpRaw (Buffer, BufferSize); + + // Move the stream forward as we have consumed the Buffer. + Status = AmlStreamProgress (FStream, BufferSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + } + + return Status; +} + +/** Parse the list of fixed arguments of the input ObjectNode. + + For each argument, create a node and add it to the fixed argument list + of the Node. + If a fixed argument has children, parse them. + + @param [in] ObjectNode Object node to parse the fixed arguments + from. + @param [in] FStream Forward stream containing the AML + bytecode to parse. + The stream must not be at its end. + @param [in] NameSpaceRefList List of namespace reference nodes. + + @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 +AmlParseFixedArguments ( + IN AML_OBJECT_NODE * ObjectNode, + IN AML_STREAM * FStream, + IN LIST_ENTRY * NameSpaceRefList + ) +{ + EFI_STATUS Status; + + AML_NODE_HEADER * FixedArgNode; + AML_STREAM FixedArgFStream; + + EAML_PARSE_INDEX TermIndex; + EAML_PARSE_INDEX MaxIndex; + CONST AML_PARSE_FORMAT * Format; + + // Fixed arguments of method invocations node are handled differently. + if (!IS_AML_OBJECT_NODE (ObjectNode) || + AmlNodeCompareOpCode (ObjectNode, AML_METHOD_INVOC_OP, 0) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (NameSpaceRefList == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + TermIndex = EAmlParseIndexTerm0; + MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount ( + (AML_OBJECT_NODE*)ObjectNode + ); + if ((ObjectNode->AmlByteEncoding != NULL) && + (ObjectNode->AmlByteEncoding->Format != NULL)) { + Format = ObjectNode->AmlByteEncoding->Format; + } else { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Parse all the FixedArgs. + while ((TermIndex < MaxIndex) && + !IS_END_OF_STREAM (FStream) && + (Format[TermIndex] != EAmlNone)) { + // Initialize a FixedArgStream to parse the current fixed argument. + Status = AmlStreamInitSubStream (FStream, &FixedArgFStream); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Parse the current fixed argument. + Status = AmlParseArgument ( + (CONST AML_NODE_HEADER*)ObjectNode, + Format[TermIndex], + &FixedArgFStream, + NameSpaceRefList, + &FixedArgNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Add the fixed argument to the parent node's fixed argument list. + // FixedArgNode can be an object or data node. + Status = AmlSetFixedArgument ( + (AML_OBJECT_NODE*)ObjectNode, + TermIndex, + FixedArgNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + // Delete the sub-tree if the insertion failed. + // Otherwise its reference will be lost. + // Use DeleteTree because if the argument was a method invocation, + // multiple nodes have been created. + AmlDeleteTree (FixedArgNode); + return Status; + } + + // Parse the AML bytecode of the FixedArgNode if this is an object node. + if (IS_AML_OBJECT_NODE (FixedArgNode) && + !IS_END_OF_STREAM (&FixedArgFStream)) { + Status = AmlParseStream ( + FixedArgNode, + &FixedArgFStream, + NameSpaceRefList + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + // Move the stream forward as we have consumed the sub-stream. + Status = AmlStreamProgress ( + FStream, + AmlStreamGetIndex (&FixedArgFStream) + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + TermIndex++; + } // while + + return EFI_SUCCESS; +} + +/** Parse the variable list of arguments of the input ObjectNode. + + For each variable argument, create a node and add it to the variable list of + arguments of the Node. + If a variable argument has children, parse them recursively. + + The arguments of method invocation nodes are added to the variable list of + arguments of the method invocation node. It is necessary to first get + the number of arguments to parse for this kind of node. A method invocation + can have at most 7 fixed arguments. + + @param [in] Node Node to parse the variable arguments + from. + @param [in] FStream Forward stream containing the AML + bytecode to parse. + The stream must not be at its end. + @param [in] NameSpaceRefList List of namespace reference nodes. + + @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 +AmlParseVariableArguments ( + IN AML_NODE_HEADER * Node, + IN AML_STREAM * FStream, + IN LIST_ENTRY * NameSpaceRefList + ) +{ + EFI_STATUS Status; + + BOOLEAN IsMethodInvocation; + UINT8 MethodInvocationArgCount; + + AML_NODE_HEADER * VarArgNode; + AML_STREAM VarArgFStream; + + if ((!AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)Node, + AML_HAS_CHILD_OBJ + ) && + !IS_AML_ROOT_NODE (Node)) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (NameSpaceRefList == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlGetMethodInvocationArgCount ( + (CONST AML_OBJECT_NODE*)Node, + &IsMethodInvocation, + &MethodInvocationArgCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Parse variable arguments while the Stream is not empty. + while (!IS_END_OF_STREAM (FStream)) { + // If the number of variable arguments are counted, decrement the counter. + if ((IsMethodInvocation) && (MethodInvocationArgCount-- == 0)) { + return EFI_SUCCESS; + } + + // Initialize a VarArgStream to parse the current variable argument. + Status = AmlStreamInitSubStream (FStream, &VarArgFStream); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Parse the current variable argument. + Status = AmlParseArgument ( + Node, + EAmlObject, + &VarArgFStream, + NameSpaceRefList, + &VarArgNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Add the variable argument to its parent variable list of arguments. + // VarArgNode can be an object or data node. + Status = AmlVarListAddTailInternal ( + (AML_NODE_HEADER*)Node, + VarArgNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + // Delete the sub-tree if the insertion failed. + // Otherwise its reference will be lost. + // Use DeleteTree because if the argument was a method invocation, + // multiple nodes have been created. + AmlDeleteTree (VarArgNode); + return Status; + } + + // Parse the AML bytecode of the VarArgNode if this is an object node. + if (IS_AML_OBJECT_NODE (VarArgNode) && + (!IS_END_OF_STREAM (&VarArgFStream))) { + Status = AmlParseStream (VarArgNode, &VarArgFStream, NameSpaceRefList); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + // Move the stream forward as we have consumed the sub-stream. + Status = AmlStreamProgress ( + FStream, + AmlStreamGetIndex (&VarArgFStream) + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } // while + + // If the number of variable arguments are counted, check all the + // MethodInvocationArgCount have been parsed. + if (IsMethodInvocation && (MethodInvocationArgCount != 0)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + return Status; +} + +/** Parse the AML stream and populate the root node. + + @param [in] RootNode RootNode to which the children are + added. + @param [in, out] FStream Forward stream containing the AML + bytecode to parse. + The stream must not be at its end. + @param [in, out] NameSpaceRefList List of namespace reference nodes. + + @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. +*/ +STATIC +EFI_STATUS +EFIAPI +AmlPopulateRootNode ( + IN AML_ROOT_NODE * RootNode, + IN OUT AML_STREAM * FStream, + IN OUT LIST_ENTRY * NameSpaceRefList + ) +{ + EFI_STATUS Status; + + if (!IS_AML_ROOT_NODE (RootNode) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (NameSpaceRefList == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // A Root Node only has variable arguments. + Status = AmlParseVariableArguments ( + (AML_NODE_HEADER*)RootNode, + FStream, + NameSpaceRefList + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Parse the AML stream an populate the object node. + + @param [in] ObjectNode ObjectNode to which the children are + added. + @param [in, out] FStream Forward stream containing the AML + bytecode to parse. + The stream must not be at its end. + @param [in, out] NameSpaceRefList List of namespace reference nodes. + + @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. +*/ +STATIC +EFI_STATUS +EFIAPI +AmlPopulateObjectNode ( + IN AML_OBJECT_NODE * ObjectNode, + IN OUT AML_STREAM * FStream, + IN OUT LIST_ENTRY * NameSpaceRefList + ) +{ + EFI_STATUS Status; + + if (!IS_AML_OBJECT_NODE (ObjectNode) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream) || + (NameSpaceRefList == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + + // Don't parse the fixed arguments of method invocation nodes. + // The AML encoding for method invocations in the ACPI specification 6.3 is: + // MethodInvocation := NameString TermArgList + // Since the AML specification does not define an OpCode for method + // invocation, this AML parser defines a pseudo opcode and redefines the + // grammar for simplicity as: + // MethodInvocation := MethodInvocationOp NameString ArgumentCount TermArgList + // ArgumentCount := ByteData + // Due to this difference, the MethodInvocationOp and the fixed argument + // i.e. ArgumentCount is not available in the AML stream and need to be + // handled differently. + if (!AmlNodeCompareOpCode (ObjectNode, AML_METHOD_INVOC_OP, 0)) { + // Parse the fixed list of arguments. + Status = AmlParseFixedArguments ( + ObjectNode, + FStream, + NameSpaceRefList + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + // Save the association [node reference/pathname] in the NameSpaceRefList. + // This allows to identify method invocations from other namespace + // paths. Method invocation need to be parsed differently. + if (AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)ObjectNode, + AML_IN_NAMESPACE)) { + Status = AmlAddNameSpaceReference ( + (CONST AML_OBJECT_NODE*)ObjectNode, + NameSpaceRefList + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + if (!IS_END_OF_STREAM (FStream)) { + // Parse the variable list of arguments if present. + if (AmlNodeHasAttribute (ObjectNode, AML_HAS_CHILD_OBJ)) { + Status = AmlParseVariableArguments ( + (AML_NODE_HEADER*)ObjectNode, + FStream, + NameSpaceRefList + ); + } else if (AmlNodeHasAttribute (ObjectNode, AML_HAS_BYTE_LIST)) { + // Parse the byte list if present. + Status = AmlParseByteList ( + ObjectNode, + FStream + ); + } else if (AmlNodeHasAttribute (ObjectNode, AML_HAS_FIELD_LIST)) { + // Parse the field list if present. + Status = AmlParseFieldList ( + ObjectNode, + FStream, + NameSpaceRefList + ); + } + + // Check status and assert + if (EFI_ERROR (Status)) { + ASSERT (0); + } + } + + return Status; +} + +/** Invoke the appropriate parsing functions based on the Node type. + + @param [in] Node Node from which the children are parsed. + Must be a root node or an object node. + @param [in] FStream Forward stream containing the AML + bytecode to parse. + The stream must not be at its end. + @param [in] NameSpaceRefList List of namespace reference nodes. + + @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. +*/ +STATIC +EFI_STATUS +EFIAPI +AmlParseStream ( + IN AML_NODE_HEADER * Node, + IN AML_STREAM * FStream, + IN LIST_ENTRY * NameSpaceRefList + ) +{ + EFI_STATUS Status; + + if (IS_AML_ROOT_NODE (Node)) { + Status = AmlPopulateRootNode ( + (AML_ROOT_NODE*)Node, + FStream, + NameSpaceRefList + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + + } else if (IS_AML_OBJECT_NODE (Node)) { + Status = AmlPopulateObjectNode ( + (AML_OBJECT_NODE*)Node, + FStream, + NameSpaceRefList + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + + } else { + // Data node or other. + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + } + + return Status; +} + +/** Parse the definition block. + + This function parses the whole AML blob. It starts with the ACPI DSDT/SSDT + header and then parses the AML bytestream. + A tree structure is returned via the RootPtr. + The tree must be deleted with the AmlDeleteTree function. + + @param [in] DefinitionBlock Pointer to the definition block. + @param [out] RootPtr Pointer to the root node of the tree. + + @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 +AmlParseDefinitionBlock ( + IN CONST EFI_ACPI_DESCRIPTION_HEADER * DefinitionBlock, + OUT AML_ROOT_NODE ** RootPtr + ) +{ + EFI_STATUS Status; + EFI_STATUS Status1; + AML_STREAM Stream; + AML_ROOT_NODE * Root; + + LIST_ENTRY NameSpaceRefList; + + UINT8 * Buffer; + UINT32 MaxBufferSize; + + if ((DefinitionBlock == NULL) || + (RootPtr == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Buffer = (UINT8*)DefinitionBlock + sizeof (EFI_ACPI_DESCRIPTION_HEADER); + if (DefinitionBlock->Length < sizeof (EFI_ACPI_DESCRIPTION_HEADER)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + MaxBufferSize = DefinitionBlock->Length - + (UINT32)sizeof (EFI_ACPI_DESCRIPTION_HEADER); + + // Create a root node. + Status = AmlCreateRootNode ( + (EFI_ACPI_DESCRIPTION_HEADER*)DefinitionBlock, + &Root + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + *RootPtr = Root; + + if (MaxBufferSize == 0) { + return EFI_SUCCESS; + } + + // Initialize a stream to parse the AML bytecode. + Status = AmlStreamInit ( + &Stream, + Buffer, + MaxBufferSize, + EAmlStreamDirectionForward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + // Initialize the NameSpaceRefList, holding references to nodes declaring + // a name in the AML namespace. + InitializeListHead (&NameSpaceRefList); + + // Parse the whole AML blob. + Status = AmlParseStream ( + (AML_NODE_HEADER*)Root, + &Stream, + &NameSpaceRefList + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + // Check the whole AML blob has been parsed. + if (!IS_END_OF_STREAM (&Stream)) { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + // Print the list of NameSpace reference nodes. + // AmlDbgPrintNameSpaceRefList (&NameSpaceRefList); + + // Delete the NameSpaceRefList + goto exit_handler; + +error_handler: + if (Root != NULL) { + AmlDeleteTree ((AML_NODE_HEADER*)Root); + } + +exit_handler: + Status1 = AmlDeleteNameSpaceRefList (&NameSpaceRefList); + if (EFI_ERROR (Status1)) { + ASSERT (0); + if (!EFI_ERROR (Status)) { + return Status1; + } + } + + return Status; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.h new file mode 100644 index 000000000..096a9596e --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlParser.h @@ -0,0 +1,72 @@ +/** @file + AML Parser. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_PARSER_H_ +#define AML_PARSER_H_ + +#include +#include + +/** Parse the list of fixed arguments of the input ObjectNode. + + For each argument, create a node and add it to the fixed argument list + of the Node. + If a fixed argument has children, parse them. + + @param [in] ObjectNode Object node to parse the fixed arguments + from. + @param [in] FStream Forward stream containing the AML + bytecode to parse. + The stream must not be at its end. + @param [in] NameSpaceRefList List of namespace reference nodes. + + @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 +AmlParseFixedArguments ( + IN AML_OBJECT_NODE * ObjectNode, + IN AML_STREAM * FStream, + IN LIST_ENTRY * NameSpaceRefList + ); + +/** Parse the variable list of arguments of the input ObjectNode. + + For each variable argument, create a node and add it to the variable list of + arguments of the Node. + If a variable argument has children, parse them recursively. + + The arguments of method invocation nodes are added to the variable list of + arguments of the method invocation node. It is necessary to first get + the number of arguments to parse for this kind of node. A method invocation + can have at most 7 fixed arguments. + + @param [in] Node Node to parse the variable arguments + from. + @param [in] FStream Forward stream containing the AML + bytecode to parse. + The stream must not be at its end. + @param [in] NameSpaceRefList List of namespace reference nodes. + + @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 +AmlParseVariableArguments ( + IN AML_NODE_HEADER * Node, + IN AML_STREAM * FStream, + IN LIST_ENTRY * NameSpaceRefList + ); + +#endif // AML_PARSER_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c new file mode 100644 index 000000000..09fb3e725 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c @@ -0,0 +1,328 @@ +/** @file + AML Resource Data Parser. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Glossary: + - Rd or RD - Resource Data + - Rds or RDS - Resource Data Small + - Rdl or RDL - Resource Data Large +**/ + +#include + +#include +#include +#include +#include + +/** Get the size of a resource data element using a stream. + + If the resource data element is of the large type, the Header + is expected to be at least 3 bytes long. + + The use of a stream makes this function safer + than the version without stream. + + @param [in] FStream Forward stream pointing to a resource data + element. + The stream must not be at its end. + + @return The size of the resource data element. + Zero if error. +**/ +UINT32 +EFIAPI +AmlRdStreamGetRdSize ( + IN CONST AML_STREAM * FStream + ) +{ + CONST AML_RD_HEADER * CurrRdElement; + + if (!IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream)) { + ASSERT (0); + return 0; + } + + CurrRdElement = (CONST AML_RD_HEADER*)AmlStreamGetCurrPos (FStream); + if (CurrRdElement == NULL) { + ASSERT (0); + return 0; + } + + // If the resource data element is of the large type, check for overflow. + if (AML_RD_IS_LARGE (CurrRdElement) && + (AmlStreamGetFreeSpace (FStream) < + sizeof (ACPI_LARGE_RESOURCE_HEADER))) { + return 0; + } + + return AmlRdGetSize (CurrRdElement); +} + +/** Check the nesting of resource data elements that are dependent + function descriptors. + + @param [in] FStream Forward stream pointing to a resource data + element. The stream is not + modified/progressing. + The stream must not be at its end. + @param [in, out] InFunctionDesc Pointer holding the nesting of the + resource data buffer. + InFunctionDesc holds TRUE if the resource + data at the address of Buffer is currently + in a dependent function descriptor list. + + @retval FALSE The Header being parsed is ending a function descriptor + list when none started. This should not be possible for a + resource data buffer. + @retval TRUE Otherwise. +**/ +STATIC +BOOLEAN +EFIAPI +AmlRdCheckFunctionDescNesting ( + IN CONST AML_STREAM * FStream, + IN OUT BOOLEAN * InFunctionDesc + ) +{ + CONST AML_RD_HEADER * CurrRdElement; + + if (!IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + (InFunctionDesc == NULL)) { + ASSERT (0); + return FALSE; + } + + CurrRdElement = AmlStreamGetCurrPos (FStream); + if (CurrRdElement == NULL) { + ASSERT (0); + return FALSE; + } + + // Starting a dependent function descriptor. + // It is possible to start one when one has already started. + if (AmlRdCompareDescId ( + CurrRdElement, + AML_RD_BUILD_SMALL_DESC_ID ( + ACPI_SMALL_START_DEPENDENT_DESCRIPTOR_NAME))) { + *InFunctionDesc = TRUE; + return TRUE; + } + + // Ending a dependent function descriptor. + if (AmlRdCompareDescId ( + CurrRdElement, + AML_RD_BUILD_SMALL_DESC_ID ( + ACPI_SMALL_END_DEPENDENT_DESCRIPTOR_NAME))) { + if (*InFunctionDesc) { + *InFunctionDesc = FALSE; + return TRUE; + } + + // It should not be possible to end a dependent function descriptor + // when none started. + return FALSE; + } + + return TRUE; +} + +/** Check whether the input stream is pointing to a valid list + of resource data elements. + + The check is based on the size of resource data elements. + This means that a buffer can pass this check with non-existing descriptor Ids + that have a correct size. + + A list of resource data elements can contain one unique resource data + element, without an end tag resource data. This is the case for + a FieldList. + + @param [in] FStream Forward stream ideally pointing to a resource + data element. The stream is not + modified/progressing. + The stream must not be at its end. + + @retval TRUE The buffer is holding a valid list of resource data elements. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlRdIsResourceDataBuffer ( + IN CONST AML_STREAM * FStream + ) +{ + EFI_STATUS Status; + UINT32 FreeSpace; + AML_STREAM SubStream; + CONST AML_RD_HEADER * CurrRdElement; + UINT32 CurrRdElementSize; + BOOLEAN InFunctionDesc; + + if (!IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream)) { + ASSERT (0); + return FALSE; + } + + // Create a sub-stream from the input stream to leave it untouched. + Status = AmlStreamInitSubStream (FStream, &SubStream); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + CurrRdElement = AmlStreamGetCurrPos (&SubStream); + if (CurrRdElement == NULL) { + ASSERT (0); + return FALSE; + } + + // The first element cannot be an end tag. + if (AmlRdCompareDescId ( + CurrRdElement, + AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) { + return FALSE; + } + + InFunctionDesc = FALSE; + while (TRUE) { + FreeSpace = AmlStreamGetFreeSpace (&SubStream); + CurrRdElement = AmlStreamGetCurrPos (&SubStream); + CurrRdElementSize = AmlRdStreamGetRdSize (&SubStream); + if ((FreeSpace == 0) || + (CurrRdElement == NULL) || + (CurrRdElementSize == 0)) { + return FALSE; + } + + if (!AmlRdCheckFunctionDescNesting (&SubStream, &InFunctionDesc)) { + return FALSE; + } + + if (CurrRdElementSize > FreeSpace) { + return FALSE; + } else if (CurrRdElementSize == FreeSpace) { + return TRUE; + } + + // TODO Might want to check the CRC when available. + // An end tag resource data element must be the last element of the list. + // Thus the function should have already returned. + if (AmlRdCompareDescId ( + CurrRdElement, + AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) { + return FALSE; + } + + Status = AmlStreamProgress (&SubStream, CurrRdElementSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + } // while + + return FALSE; +} + +/** Parse a ResourceDataBuffer. + + For each resource data element, create a data node + and add them to the variable list of arguments of the BufferNode. + + The input stream is expected to point to a valid list of resource data + elements. A function is available to check it for the caller. + + @param [in] BufferNode Buffer node. + @param [in] FStream Forward stream pointing to a resource data + element. + The stream must not be at its end. + + @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 +AmlParseResourceData ( + IN AML_OBJECT_NODE * BufferNode, + IN AML_STREAM * FStream + ) +{ + EFI_STATUS Status; + AML_DATA_NODE * NewNode; + UINT32 FreeSpace; + CONST AML_RD_HEADER * CurrRdElement; + UINT32 CurrRdElementSize; + + // Check that BufferNode is an ObjectNode and has a ByteList. + if (!AmlNodeHasAttribute (BufferNode, AML_HAS_BYTE_LIST) || + !IS_STREAM (FStream) || + IS_END_OF_STREAM (FStream) || + !IS_STREAM_FORWARD (FStream)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Iterate through the resource data elements and create nodes. + // We assume the Buffer has already been validated as a list of + // resource data elements, so less checks are made. + while (TRUE) { + FreeSpace = AmlStreamGetFreeSpace (FStream); + if (FreeSpace == 0) { + break; + } + + CurrRdElement = (CONST AML_RD_HEADER*)AmlStreamGetCurrPos (FStream); + CurrRdElementSize = AmlRdStreamGetRdSize (FStream); + + Status = AmlCreateDataNode ( + EAmlNodeDataTypeResourceData, + (CONST UINT8*)CurrRdElement, + CurrRdElementSize, + &NewNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlVarListAddTailInternal ( + (AML_NODE_HEADER*)BufferNode, + (AML_NODE_HEADER*)NewNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + AmlDeleteTree ((AML_NODE_HEADER*)NewNode); + return Status; + } + + Status = AmlStreamProgress (FStream, CurrRdElementSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + DumpRaw (CurrRdElement, CurrRdElementSize); + + // Exit the loop when finding the resource data end tag. + if (AmlRdCompareDescId ( + CurrRdElement, + AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) { + if (FreeSpace != CurrRdElementSize) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + break; + } + } // while + + return EFI_SUCCESS; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.h new file mode 100644 index 000000000..13dfb352c --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.h @@ -0,0 +1,71 @@ +/** @file + AML Resource Data Parser. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Glossary: + - Rd or RD - Resource Data + - Rds or RDS - Resource Data Small + - Rdl or RDL - Resource Data Large +**/ + +#ifndef AML_RESOURCE_DATA_PARSER_H_ +#define AML_RESOURCE_DATA_PARSER_H_ + +#include +#include +#include + +/** Check whether the input stream is pointing to a valid list + of resource data elements. + + The check is based on the size of resource data elements. + This means that a buffer can pass this check with non-existing descriptor Ids + that have a correct size. + + A list of resource data elements can contain one unique resource data + element, without an end tag resource data. This is the case for + a FieldList. + + @param [in] FStream Forward stream ideally pointing to a resource + data element. The stream is not + modified/progressing. + The stream must not be at its end. + + @retval TRUE The buffer is holding a valid list of resource data elements. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlRdIsResourceDataBuffer ( + IN CONST AML_STREAM * FStream + ); + +/** Parse a ResourceDataBuffer. + + For each resource data element, create a data node + and add them to the variable list of arguments of the BufferNode. + + The input stream is expected to point to a valid list of resource data + elements. A function is available to check it for the caller. + + @param [in] BufferNode Buffer node. + @param [in] FStream Forward stream pointing to a resource data + element. + The stream must not be at its end. + + @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 +AmlParseResourceData ( + IN AML_OBJECT_NODE * BufferNode, + IN AML_STREAM * FStream + ); + +#endif // AML_RESOURCE_DATA_PARSER_H_ + diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.c new file mode 100644 index 000000000..8b46c7232 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.c @@ -0,0 +1,103 @@ +/** @file + AML Resource Data. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Glossary: + - Rd or RD - Resource Data + - Rds or RDS - Resource Data Small + - Rdl or RDL - Resource Data Large +**/ + +#include + +/** Check whether the resource data has the input descriptor Id. + + The small/large bit is included in the descriptor Id, + but the size bits are not included for small resource data elements. + + @param [in] Header Pointer to the first byte of a resource data + element. + @param [in] DescriptorId The descriptor to check against. + + @retval TRUE The resource data has the descriptor Id. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlRdCompareDescId ( + IN CONST AML_RD_HEADER * Header, + IN AML_RD_HEADER DescriptorId + ) +{ + if (Header == NULL) { + ASSERT (0); + return FALSE; + } + + if (AML_RD_IS_LARGE (Header)) { + return ((*Header ^ DescriptorId) == 0); + } else { + return (((*Header & AML_RD_SMALL_ID_MASK) ^ DescriptorId) == 0); + } +} + +/** Get the descriptor Id of the resource data. + + The small/large bit is included in the descriptor Id, + but the size bits are not included for small resource data elements. + + @param [in] Header Pointer to the first byte of a resource data. + + @return A descriptor Id. +**/ +AML_RD_HEADER +EFIAPI +AmlRdGetDescId ( + IN CONST AML_RD_HEADER * Header + ) +{ + if (Header == NULL) { + ASSERT (0); + return FALSE; + } + + if (AML_RD_IS_LARGE (Header)) { + return *Header; + } + + // Header is a small resource data element. + return *Header & AML_RD_SMALL_ID_MASK; +} + +/** Get the size of a resource data element. + + If the resource data element is of the large type, the Header + is expected to be at least 3 bytes long. + + @param [in] Header Pointer to the first byte of a resource data. + + @return The size of the resource data element. +**/ +UINT32 +EFIAPI +AmlRdGetSize ( + IN CONST AML_RD_HEADER * Header + ) +{ + if (Header == NULL) { + ASSERT (0); + return FALSE; + } + + if (AML_RD_IS_LARGE (Header)) { + return ((ACPI_LARGE_RESOURCE_HEADER*)Header)->Length + + sizeof (ACPI_LARGE_RESOURCE_HEADER); + } + + // Header is a small resource data element. + return ((ACPI_SMALL_RESOURCE_HEADER*)Header)->Bits.Length + + sizeof (ACPI_SMALL_RESOURCE_HEADER); +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.h new file mode 100644 index 000000000..48e4e2aad --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/ResourceData/AmlResourceData.h @@ -0,0 +1,174 @@ +/** @file + AML Resource Data. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Glossary: + - Rd or RD - Resource Data + - Rds or RDS - Resource Data Small + - Rdl or RDL - Resource Data Large +**/ + +#ifndef AML_RESOURCE_DATA_H_ +#define AML_RESOURCE_DATA_H_ + +/* This header file does not include internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. The node definitions + must be included by the caller file. The function prototypes must + only expose AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, etc. node + definitions. + This allows to keep the functions defined here both internal and + potentially external. If necessary, any function of this file can + be exposed externally. + The Api folder is internal to the AmlLib, but should only use these + functions. They provide a "safe" way to interact with the AmlLib. +*/ + +#include +#include + +/** + @defgroup ResourceDataLibrary Resource data library + @ingroup AMLLib + @{ + Resource data are defined in the ACPI 6.3 specification, + s6.4 "Resource Data Types for ACPI". They can be created in ASL via the + ResourceTemplate () statement, cf s19.3.3 "ASL Resource Templates". + + Resource data can be of the small or large type. The difference between + small and large resource data elements is their encoding. + + Resource data are stored in the variable list of arguments of object + nodes. + @} +*/ + +/** Resource Descriptor header for Small/Large Resource Data Object. + This is the first byte of a Small/Large Resource Data element. + + Can be a ACPI_SMALL_RESOURCE_HEADER or ACPI_LARGE_RESOURCE_HEADER. + + @ingroup ResourceDataStructures +*/ +typedef UINT8 AML_RD_HEADER; + +/** Mask for the small resource data size. + + @ingroup ResourceDataStructures +*/ +#define AML_RD_SMALL_SIZE_MASK (0x7U) + +/** Mask for the small resource data ID. + + @ingroup ResourceDataStructures +*/ +#define AML_RD_SMALL_ID_MASK (0xFU << 3) + +/** Mask for the large resource data ID. + + @ingroup ResourceDataStructures +*/ +#define AML_RD_LARGE_ID_MASK (0x7FU) + +/** + @defgroup ResourceDataApis Resource data APIs + @ingroup ResourceDataLibrary + @{ + Resource data APIs allow to manipulate/decode resource data elements. + @} +*/ + +/** Check whether a resource data is of the large type. + + @ingroup ResourceDataApis + + @param [in] Header Pointer to the first byte of a resource data. + + @retval TRUE If the resource data is of the large type. + @retval FALSE Otherwise. +**/ +#define AML_RD_IS_LARGE(Header) \ + (((ACPI_SMALL_RESOURCE_HEADER*)Header)->Bits.Type == \ + ACPI_LARGE_ITEM_FLAG) + +/** Build a small resource data descriptor Id. + The small/large bit is included in the descriptor Id, + but the size bits are not included. + + @ingroup ResourceDataApis + + @param [in] Id Descriptor Id. + + @return A descriptor Id. +**/ +#define AML_RD_BUILD_SMALL_DESC_ID(Id) ((AML_RD_HEADER)((Id & 0xF) << 3)) + +/** Build a large resource data descriptor Id. + The small/large bit is included in the descriptor Id. + + @ingroup ResourceDataApis + + @param [in] Id Id of the descriptor. + + @return A descriptor Id. +**/ +#define AML_RD_BUILD_LARGE_DESC_ID(Id) ((AML_RD_HEADER)((BIT7) | Id)) + +/** Check whether the resource data has the input descriptor Id. + + The small/large bit is included in the descriptor Id, + but the size bits are not included for small resource data elements. + + @ingroup ResourceDataApis + + @param [in] Header Pointer to the first byte of a resource data + element. + @param [in] DescriptorId The descriptor to check against. + + @retval TRUE The resource data has the descriptor Id. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlRdCompareDescId ( + IN CONST AML_RD_HEADER * Header, + IN AML_RD_HEADER DescriptorId + ); + +/** Get the descriptor Id of the resource data. + + The small/large bit is included in the descriptor Id, + but the size bits are not included for small resource data elements. + + @ingroup ResourceDataApis + + @param [in] Header Pointer to the first byte of a resource data. + + @return A descriptor Id. +**/ +AML_RD_HEADER +EFIAPI +AmlRdGetDescId ( + IN CONST AML_RD_HEADER * Header + ); + +/** Get the size of a resource data element. + + If the resource data element is of the large type, the Header + is expected to be at least 3 bytes long. + + @ingroup ResourceDataApis + + @param [in] Header Pointer to the first byte of a resource data. + + @return The size of the resource data element. +**/ +UINT32 +EFIAPI +AmlRdGetSize ( + IN CONST AML_RD_HEADER * Header + ); + +#endif // AML_RESOURCE_DATA_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Serialize/AmlSerialize.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Serialize/AmlSerialize.c new file mode 100644 index 000000000..2a47c229d --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Serialize/AmlSerialize.c @@ -0,0 +1,324 @@ +/** @file + AML Serialize. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include +#include +#include + +/** Callback function to copy the AML bytecodes contained in a node + to the Stream stored in the Context. + The SDT header data contained in the root node is not serialized + by this function. + + @param [in] Node Pointer to the node to copy the AML bytecodes + from. + @param [in, out] Context Contains a forward Stream to write to. + (AML_STREAM*)Context. + @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 +AmlSerializeNodeCallback ( + IN AML_NODE_HEADER * Node, + IN OUT VOID * Context, OPTIONAL + IN OUT EFI_STATUS * Status OPTIONAL + ) +{ + EFI_STATUS Status1; + + CONST AML_DATA_NODE * DataNode; + CONST AML_OBJECT_NODE * ObjectNode; + AML_STREAM * FStream; + + // Bytes needed to store OpCode[1] + SubOpcode[1] + MaxPkgLen[4] = 6 bytes. + UINT8 ObjectNodeInfoArray[6]; + UINT32 Index; + BOOLEAN ContinueEnum; + + CONST AML_OBJECT_NODE * ParentNode; + EAML_PARSE_INDEX IndexPtr; + + if (!IS_AML_NODE_VALID (Node) || + (Context == NULL)) { + ASSERT (0); + Status1 = EFI_INVALID_PARAMETER; + ContinueEnum = FALSE; + goto error_handler; + } + + // Ignore the second fixed argument of method invocation nodes + // as the information stored there (the argument count) is not in the + // ACPI specification. + ParentNode = (CONST AML_OBJECT_NODE*)AmlGetParent ((AML_NODE_HEADER*)Node); + if (IS_AML_OBJECT_NODE (ParentNode) && + AmlNodeCompareOpCode (ParentNode, AML_METHOD_INVOC_OP, 0) && + AmlIsNodeFixedArgument (Node, &IndexPtr)) { + if (IndexPtr == EAmlParseIndexTerm1) { + if (Status != NULL) { + *Status = EFI_SUCCESS; + } + return TRUE; + } + } + + Status1 = EFI_SUCCESS; + ContinueEnum = TRUE; + FStream = (AML_STREAM*)Context; + + if (IS_AML_DATA_NODE (Node)) { + // Copy the content of the Buffer for a DataNode. + DataNode = (AML_DATA_NODE*)Node; + Status1 = AmlStreamWrite ( + FStream, + DataNode->Buffer, + DataNode->Size + ); + if (EFI_ERROR (Status1)) { + ASSERT (0); + ContinueEnum = FALSE; + goto error_handler; + } + + } else if (IS_AML_OBJECT_NODE (Node) && + !AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)Node, + AML_IS_PSEUDO_OPCODE)) { + // Ignore pseudo-opcodes as they are not part of the + // ACPI specification. + + ObjectNode = (AML_OBJECT_NODE*)Node; + + Index = 0; + // Copy the opcode(s). + ObjectNodeInfoArray[Index++] = ObjectNode->AmlByteEncoding->OpCode; + if (ObjectNode->AmlByteEncoding->OpCode == AML_EXT_OP) { + ObjectNodeInfoArray[Index++] = ObjectNode->AmlByteEncoding->SubOpCode; + } + + // Copy the PkgLen. + if (AmlNodeHasAttribute (ObjectNode, AML_HAS_PKG_LENGTH)) { + Index += AmlSetPkgLength ( + ObjectNode->PkgLen, + &ObjectNodeInfoArray[Index] + ); + } + + Status1 = AmlStreamWrite ( + FStream, + ObjectNodeInfoArray, + Index + ); + if (EFI_ERROR (Status1)) { + ASSERT (0); + ContinueEnum = FALSE; + goto error_handler; + } + } // IS_AML_OBJECT_NODE (Node) + +error_handler: + if (Status != NULL) { + *Status = Status1; + } + return ContinueEnum; +} + +/** Serialize a tree to create an ACPI DSDT/SSDT table. + + If: + - the content of BufferSize is >= to the size needed to serialize the + definition block; + - Buffer is not NULL; + first serialize the ACPI DSDT/SSDT header from the root node, + then serialize the AML blob from the rest of the tree. + + The content of BufferSize is always updated to the size needed to + serialize the definition block. + + @param [in] RootNode Pointer to a root node. + @param [in] Buffer Buffer to write the DSDT/SSDT table to. + If Buffer is NULL, the size needed to + serialize the DSDT/SSDT table is returned + in BufferSize. + @param [in, out] BufferSize Pointer holding the size of the Buffer. + Its content is always updated to the size + needed to serialize the DSDT/SSDT table. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. +**/ +EFI_STATUS +EFIAPI +AmlSerializeTree ( + IN AML_ROOT_NODE * RootNode, + IN UINT8 * Buffer, OPTIONAL + IN OUT UINT32 * BufferSize + ) +{ + EFI_STATUS Status; + AML_STREAM FStream; + UINT32 TableSize; + + if (!IS_AML_ROOT_NODE (RootNode) || + (BufferSize == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Compute the total size of the AML blob. + Status = AmlComputeSize ( + (CONST AML_NODE_HEADER*)RootNode, + &TableSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Add the size of the ACPI header. + TableSize += (UINT32)sizeof (EFI_ACPI_DESCRIPTION_HEADER); + + // Check the size against the SDT header. + // The Length field in the SDT Header is updated if the tree has + // been modified. + if (TableSize != RootNode->SdtHeader->Length) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Buffer is not big enough, or NULL. + if ((TableSize < *BufferSize) || (Buffer == NULL)) { + *BufferSize = TableSize; + return EFI_SUCCESS; + } + + // Initialize the stream to the TableSize that is needed. + Status = AmlStreamInit ( + &FStream, + Buffer, + TableSize, + EAmlStreamDirectionForward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Serialize the header. + Status = AmlStreamWrite ( + &FStream, + (UINT8*)RootNode->SdtHeader, + sizeof (EFI_ACPI_DESCRIPTION_HEADER) + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = EFI_SUCCESS; + AmlEnumTree ( + (AML_NODE_HEADER*)RootNode, + AmlSerializeNodeCallback, + (VOID*)&FStream, + &Status + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Update the checksum. + return AcpiPlatformChecksum ((EFI_ACPI_DESCRIPTION_HEADER*)Buffer); +} + +/** Serialize an AML definition block. + + This functions allocates memory with the "AllocateZeroPool ()" + function. This memory is used to serialize the AML tree and is + returned in the Table. + + @param [in] RootNode Root node of the tree. + @param [out] Table On return, hold the serialized + definition block. + + @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 +AmlSerializeDefinitionBlock ( + IN AML_ROOT_NODE * RootNode, + OUT EFI_ACPI_DESCRIPTION_HEADER ** Table + ) +{ + EFI_STATUS Status; + UINT8 * TableBuffer; + UINT32 TableSize; + + if (!IS_AML_ROOT_NODE (RootNode) || + (Table == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *Table = NULL; + TableBuffer = NULL; + TableSize = 0; + + // Get the size of the SSDT table. + Status = AmlSerializeTree ( + RootNode, + TableBuffer, + &TableSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + TableBuffer = (UINT8*)AllocateZeroPool (TableSize); + if (TableBuffer == NULL) { + DEBUG (( + DEBUG_ERROR, + "ERROR: Failed to allocate memory for Table Buffer." + )); + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + // Serialize the tree to a SSDT table. + Status = AmlSerializeTree ( + RootNode, + TableBuffer, + &TableSize + ); + if (EFI_ERROR (Status)) { + FreePool (TableBuffer); + ASSERT (0); + } else { + // Save the allocated Table buffer in the table list + *Table = (EFI_ACPI_DESCRIPTION_HEADER*)TableBuffer; + } + + return Status; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.c new file mode 100644 index 000000000..419e796e5 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.c @@ -0,0 +1,665 @@ +/** @file + AML Stream. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +/** Initialize a stream. + + @param [in, out] Stream Pointer to the stream to initialize. + @param [in] Buffer Buffer to initialize Stream with. + Point to the beginning of the Buffer. + @param [in] MaxBufferSize Maximum size of Buffer. + @param [in] Direction Direction Stream is progressing + (forward, backward). + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlStreamInit ( + IN OUT AML_STREAM * Stream, + IN UINT8 * Buffer, + IN UINT32 MaxBufferSize, + IN EAML_STREAM_DIRECTION Direction + ) +{ + if ((Stream == NULL) || + (Buffer == NULL) || + (MaxBufferSize == 0) || + ((Direction != EAmlStreamDirectionForward) && + (Direction != EAmlStreamDirectionBackward))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Stream->Buffer = Buffer; + Stream->MaxBufferSize = MaxBufferSize; + Stream->Index = 0; + Stream->Direction = Direction; + + return EFI_SUCCESS; +} + +/** Clone a stream. + + Cloning a stream means copying all the values of the input Stream + in the ClonedStream. + + @param [in] Stream Pointer to the stream to clone. + @param [in] ClonedStream Pointer to the stream to initialize. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlStreamClone ( + IN CONST AML_STREAM * Stream, + OUT AML_STREAM * ClonedStream + ) +{ + if (!IS_STREAM (Stream) || + (ClonedStream == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ClonedStream->Buffer = Stream->Buffer; + ClonedStream->MaxBufferSize = Stream->MaxBufferSize; + ClonedStream->Index = Stream->Index; + ClonedStream->Direction = Stream->Direction; + + return EFI_SUCCESS; +} + +/** Initialize a sub-stream from a stream. + + A sub-stream is a stream initialized at the current position of the input + stream: + - the Buffer field points to the current position of the input stream; + - the Index field is set to 0; + - the MaxBufferSize field is set to the remaining size of the input stream; + - the direction is conserved; + + E.g.: For a forward stream: + +----------------+----------------+ + |ABCD.........XYZ| Free Space | + +----------------+----------------+ + ^ ^ ^ + Stream: Buffer CurrPos EndOfBuff + Sub-stream: Buffer/CurrPos EndOfBuff + + @param [in] Stream Pointer to the stream from which a sub-stream is + created. + @param [in] SubStream Pointer to the stream to initialize. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlStreamInitSubStream ( + IN CONST AML_STREAM * Stream, + OUT AML_STREAM * SubStream + ) +{ + if (!IS_STREAM (Stream) || + (SubStream == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (IS_STREAM_FORWARD (Stream)) { + SubStream->Buffer = AmlStreamGetCurrPos (Stream); + } else if (IS_STREAM_BACKWARD (Stream)) { + SubStream->Buffer = Stream->Buffer; + } else { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + SubStream->MaxBufferSize = AmlStreamGetFreeSpace (Stream); + SubStream->Index = 0; + SubStream->Direction = Stream->Direction; + + return EFI_SUCCESS; +} + +/** Get the buffer of a stream. + + @param [in] Stream Pointer to a stream. + + @return The stream's Buffer. + NULL otherwise. +**/ +UINT8 * +EFIAPI +AmlStreamGetBuffer ( + IN CONST AML_STREAM * Stream + ) +{ + if (!IS_STREAM (Stream)) { + ASSERT (0); + return NULL; + } + return Stream->Buffer; +} + +/** Get the size of Stream's Buffer. + + @param [in] Stream Pointer to a stream. + + @return The Size of Stream's Buffer. + Return 0 if Stream is invalid. +**/ +UINT32 +EFIAPI +AmlStreamGetMaxBufferSize ( + IN CONST AML_STREAM * Stream + ) +{ + if (!IS_STREAM (Stream)) { + ASSERT (0); + return 0; + } + return Stream->MaxBufferSize; +} + +/** Reduce the maximal size of Stream's Buffer (MaxBufferSize field). + + @param [in] Stream Pointer to a stream. + @param [in] Diff Value to subtract to the Stream's MaxBufferSize. + 0 < x < MaxBufferSize - Index. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlStreamReduceMaxBufferSize ( + IN AML_STREAM * Stream, + IN UINT32 Diff + ) +{ + if (!IS_STREAM (Stream) || + (Diff == 0) || + ((Stream->MaxBufferSize - Diff) <= Stream->Index)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Stream->MaxBufferSize -= Diff; + return EFI_SUCCESS; +} + +/** Get Stream's Index. + + Stream's Index is incremented when writing data, reading data, + or moving the position in the Stream. + It can be seen as an index: + - starting at the beginning of Stream's Buffer if the stream goes forward; + - starting at the end of Stream's Buffer if the stream goes backward. + + @param [in] Stream Pointer to a stream. + + @return Stream's Index. + Return 0 if Stream is invalid. +**/ +UINT32 +EFIAPI +AmlStreamGetIndex ( + IN CONST AML_STREAM * Stream + ) +{ + if (!IS_STREAM (Stream)) { + ASSERT (0); + return 0; + } + return Stream->Index; +} + +/** Get Stream's Direction. + + @param [in] Stream Pointer to a stream. + + @return Stream's Direction. + Return EAmlStreamDirectionUnknown if Stream is invalid. +**/ +EAML_STREAM_DIRECTION +EFIAPI +AmlStreamGetDirection ( + IN CONST AML_STREAM * Stream + ) +{ + if (!IS_STREAM (Stream)) { + ASSERT (0); + return EAmlStreamDirectionInvalid; + } + return Stream->Direction; +} + +/** Return a pointer to the current position in the stream. + + @param [in] Stream Pointer to a stream. + + @return The current position in the stream. + Return NULL if error. +**/ +UINT8 * +EFIAPI +AmlStreamGetCurrPos ( + IN CONST AML_STREAM * Stream + ) +{ + if (!IS_STREAM (Stream)) { + ASSERT (0); + return NULL; + } + + if (IS_STREAM_FORWARD (Stream)) { + return Stream->Buffer + Stream->Index; + } else if (IS_STREAM_BACKWARD (Stream)) { + return Stream->Buffer + (Stream->MaxBufferSize - 1) - Stream->Index; + } else { + ASSERT (0); + return NULL; + } +} + +/** Get the space available in the stream. + + @param [in] Stream Pointer to a stream. + + @return Remaining space available in the stream. + Zero in case of error or if the stream is at its end. +**/ +UINT32 +EFIAPI +AmlStreamGetFreeSpace ( + IN CONST AML_STREAM * Stream + ) +{ + if (!IS_STREAM (Stream)) { + ASSERT (0); + return 0; + } + + if (Stream->Index > Stream->MaxBufferSize) { + ASSERT (0); + return 0; + } + + return Stream->MaxBufferSize - Stream->Index; +} + +/** Move Stream by Offset bytes. + + The stream current position is moved according to the stream direction + (forward, backward). + + @param [in] Stream Pointer to a stream. + The stream must not be at its end. + @param [in] Offset Offset to move the stream of. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. +**/ +EFI_STATUS +EFIAPI +AmlStreamProgress ( + IN AML_STREAM * Stream, + IN UINT32 Offset + ) +{ + if (!IS_STREAM (Stream) || + IS_END_OF_STREAM (Stream) || + (Offset == 0)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (AmlStreamGetFreeSpace (Stream) < Offset) { + ASSERT (0); + return EFI_BUFFER_TOO_SMALL; + } + + Stream->Index += Offset; + + return EFI_SUCCESS; +} + +/** Rewind Stream of Offset bytes. + + The stream current position is rewound according to the stream direction + (forward, backward). A stream going forward will be rewound backward. + + @param [in] Stream Pointer to a stream. + @param [in] Offset Offset to rewind the stream of. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. +**/ +EFI_STATUS +EFIAPI +AmlStreamRewind ( + IN AML_STREAM * Stream, + IN UINT32 Offset + ) +{ + if (!IS_STREAM (Stream) || + (Offset == 0)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (AmlStreamGetIndex (Stream) < Offset) { + ASSERT (0); + return EFI_BUFFER_TOO_SMALL; + } + + Stream->Index -= Offset; + + return EFI_SUCCESS; +} + +/** Reset the Stream (move the current position to the initial position). + + @param [in] Stream Pointer to a stream. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlStreamReset ( + IN AML_STREAM * Stream + ) +{ + if (!IS_STREAM (Stream)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Stream->Index = 0; + + return EFI_SUCCESS; +} + +/** Peek one byte at Stream's current position. + + Stream's position is not moved when peeking. + + @param [in] Stream Pointer to a stream. + The stream must not be at its end. + @param [out] OutByte Pointer holding the byte value of + the stream current position. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. +**/ +EFI_STATUS +EFIAPI +AmlStreamPeekByte ( + IN AML_STREAM * Stream, + OUT UINT8 * OutByte + ) +{ + UINT8 * CurPos; + + if (!IS_STREAM (Stream) || + IS_END_OF_STREAM (Stream) || + (OutByte == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + CurPos = AmlStreamGetCurrPos (Stream); + if (CurPos == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutByte = *CurPos; + return EFI_SUCCESS; +} + +/** Read one byte at Stream's current position. + + The stream current position is moved when reading. + + @param [in] Stream Pointer to a stream. + The stream must not be at its end. + @param [out] OutByte Pointer holding the byte value of + the stream current position. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. +**/ +EFI_STATUS +EFIAPI +AmlStreamReadByte ( + IN AML_STREAM * Stream, + OUT UINT8 * OutByte + ) +{ + EFI_STATUS Status; + + if (!IS_STREAM (Stream) || + IS_END_OF_STREAM (Stream) || + (OutByte == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Stream is checked in the function call. + Status = AmlStreamPeekByte (Stream, OutByte); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlStreamProgress (Stream, 1); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Write Size bytes in the stream. + + If the stream goes backward (toward lower addresses), the bytes written + to the stream are not reverted. + In the example below, writing "Hello" to the stream will not revert + the string. The end of the stream buffer will contain "Hello world!". + Stream buffer: + +---------------+-----+-----+-----+-----+-----+-----+---- +------+ + | ..... | ' ' | 'w' | 'o' | 'r' | 'l' | 'd' | '!' | '\0' | + +---------------+-----+-----+-----+-----+-----+-----+---- +------+ + ^ + Current position. + + @param [in] Stream Pointer to a stream. + The stream must not be at its end. + @param [in] Buffer Pointer to the data to write. + @param [in] Size Number of bytes to write. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. +**/ +EFI_STATUS +EFIAPI +AmlStreamWrite ( + IN AML_STREAM * Stream, + IN CONST UINT8 * Buffer, + IN UINT32 Size + ) +{ + UINT8 * CurrPos; + + if (!IS_STREAM (Stream) || + IS_END_OF_STREAM (Stream) || + (Buffer == NULL) || + (Size == 0)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (AmlStreamGetFreeSpace (Stream) < Size) { + ASSERT (0); + return EFI_BUFFER_TOO_SMALL; + } + + CurrPos = AmlStreamGetCurrPos (Stream); + + // If the Stream goes backward, prepare some space to copy the data. + if (IS_STREAM_BACKWARD (Stream)) { + CurrPos -= Size; + } + + CopyMem (CurrPos, Buffer, Size); + Stream->Index += Size; + + return EFI_SUCCESS; +} + +/** Compare Size bytes between Stream1 and Stream2 from their + respective current position. + + Stream1 and Stream2 must go in the same direction. + Stream1 and Stream2 are left unchanged. + + @param [in] Stream1 First stream to compare. + The stream must not be at its end. + @param [in] Stream2 Second stream to compare. + The stream must not be at its end. + @param [in] Size Number of bytes to compare. + Must be lower than the minimum remaining space of + Stream1 and Stream2. + Must be non-zero. + + @retval TRUE If Stream1 and Stream2 have Size bytes equal, + from their respective current position. + The function completed successfully. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlStreamCmp ( + IN CONST AML_STREAM * Stream1, + IN CONST AML_STREAM * Stream2, + IN UINT32 Size + ) +{ + UINT32 MinSize; + UINT8 * CurrPosStream1; + UINT8 * CurrPosStream2; + + if (!IS_STREAM (Stream1) || + IS_END_OF_STREAM (Stream1) || + !IS_STREAM (Stream2) || + IS_END_OF_STREAM (Stream2) || + (Stream1->Direction != Stream2->Direction) || + (Size == 0)) { + ASSERT (0); + return FALSE; + } + + // Check the Size is not longer than the remaining size of + // Stream1 and Stream2. + MinSize = MIN ( + AmlStreamGetFreeSpace (Stream1), + AmlStreamGetFreeSpace (Stream2) + ); + if (MinSize < Size) { + ASSERT (0); + return FALSE; + } + + CurrPosStream1 = AmlStreamGetCurrPos (Stream1); + if (CurrPosStream1 == NULL) { + ASSERT (0); + return FALSE; + } + CurrPosStream2 = AmlStreamGetCurrPos (Stream2); + if (CurrPosStream2 == NULL) { + ASSERT (0); + return FALSE; + } + + if (Stream1->Direction == EAmlStreamDirectionForward) { + return (0 == CompareMem (CurrPosStream1, CurrPosStream2, MinSize)); + } + + // The stream is already pointing on the last byte, thus the (-1). + // +---------------------+ + // BStream | | | | | | | |M|E|T|0| + // +---------------------+ + // ^ + // CurrPos + return (0 == CompareMem ( + CurrPosStream1 - (MinSize - 1), + CurrPosStream2 - (MinSize - 1), + MinSize + )); +} + +/** Copy Size bytes of the stream's data to DstBuffer. + + For a backward stream, the bytes are copied starting from the + current stream position. + + @param [out] DstBuffer Destination Buffer to copy the data to. + @param [in] MaxDstBufferSize Maximum size of DstBuffer. + Must be non-zero. + @param [in] Stream Pointer to the stream to copy the data from. + @param [in] Size Number of bytes to copy from the stream + buffer. + Must be lower than MaxDstBufferSize. + Must be lower than Stream's MaxBufferSize. + Return success if zero. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlStreamCpyS ( + OUT CHAR8 * DstBuffer, + IN UINT32 MaxDstBufferSize, + IN AML_STREAM * Stream, + IN UINT32 Size + ) +{ + CHAR8 * StreamBufferStart; + + // Stream is checked in the function call. + if ((DstBuffer == NULL) || + (MaxDstBufferSize == 0) || + (Size > MaxDstBufferSize) || + (Size > AmlStreamGetMaxBufferSize (Stream))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (Size == 0) { + return EFI_SUCCESS; + } + + // Find the address at which the data is starting. + StreamBufferStart = (CHAR8*)(IS_STREAM_FORWARD (Stream) ? + Stream->Buffer : + AmlStreamGetCurrPos (Stream)); + + CopyMem (DstBuffer, StreamBufferStart, Size); + + return EFI_SUCCESS; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.h new file mode 100644 index 000000000..cd2da89b0 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Stream/AmlStream.h @@ -0,0 +1,451 @@ +/** @file + AML Stream. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_STREAM_H_ +#define AML_STREAM_H_ + +#include + +/** Stream direction. + + Enum to choose the direction the stream is progressing. +*/ +typedef enum EAmlStreamDirection { + EAmlStreamDirectionInvalid, ///< Invalid AML Stream direction. + EAmlStreamDirectionForward, ///< Forward direction. + /// The Stream goes toward higher addresses. + EAmlStreamDirectionBackward, ///< Forward direction. + /// The Stream goes toward lower addresses. + EAmlStreamDirectionMax, ///< Max enum. +} EAML_STREAM_DIRECTION; + +/** Stream. + + This structure is used as a wrapper around a buffer. It allows to do common + buffer manipulations (read, write, etc.) while preventing buffer overflows. +*/ +typedef struct AmlStream { + /// Pointer to a buffer. + UINT8 * Buffer; + + /// Size of Buffer. + UINT32 MaxBufferSize; + + /// Index in the Buffer. + /// The Index field allows to keep track of how many bytes have been + /// read/written in the Buffer, and to retrieve the current stream position. + /// 0 <= Index <= MaxBufferSize. + /// If Index == MaxBufferSize, no more action is allowed on the stream. + UINT32 Index; + + /// The direction the stream is progressing. + /// If the stream goes backward (toward lower addresses), the bytes written + /// to the stream are not reverted. + /// In the example below, writing "Hello" to the stream will not revert + /// the string. The end of the stream buffer will contain "Hello world!". + /// Similarly, moving the stream position will be done according to the + /// direction of the stream. + /// Stream buffer: + /// +---------------+-----+-----+-----+-----+-----+-----+---- +------+ + /// |-------------- | ' ' | 'w' | 'o' | 'r' | 'l' | 'd' | '!' | '\0' | + /// +---------------+-----+-----+-----+-----+-----+-----+---- +------+ + /// ^ + /// Current position. + EAML_STREAM_DIRECTION Direction; +} AML_STREAM; + +/** Check whether a StreamPtr is a valid Stream. + + @param [in] Stream Pointer to a stream. + + @retval TRUE Stream is a pointer to a stream. + @retval FALSE Otherwise. +*/ +#define IS_STREAM(Stream) ( \ + (((AML_STREAM*)Stream) != NULL) && \ + (((AML_STREAM*)Stream)->Buffer != NULL)) + +/** Check whether a Stream is at the end of its buffer. + + @param [in] Stream Pointer to a stream. + + @retval TRUE Stream is a pointer to a non-full stream. + @retval FALSE Otherwise. +*/ +#define IS_END_OF_STREAM(Stream) ( \ + (((AML_STREAM*)Stream)->Index == \ + ((AML_STREAM*)Stream)->MaxBufferSize)) + +/** Check Stream goes forward. + + @param [in] Stream Pointer to a stream. + + @retval TRUE Stream goes forward. + @retval FALSE Otherwise. +*/ +#define IS_STREAM_FORWARD(Stream) ( \ + ((AML_STREAM*)Stream)->Direction == EAmlStreamDirectionForward) + +/** Check Stream goes backward. + + @param [in] Stream Pointer to a stream. + + @retval TRUE Stream goes backward. + @retval FALSE Otherwise. +*/ +#define IS_STREAM_BACKWARD(Stream) ( \ + ((AML_STREAM*)Stream)->Direction == EAmlStreamDirectionBackward) + +/** Initialize a stream. + + @param [in, out] Stream Pointer to the stream to initialize. + @param [in] Buffer Buffer to initialize Stream with. + Point to the beginning of the Buffer. + @param [in] MaxBufferSize Maximum size of Buffer. + @param [in] Direction Direction Stream is progressing + (forward, backward). + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlStreamInit ( + IN OUT AML_STREAM * Stream, + IN UINT8 * Buffer, + IN UINT32 MaxBufferSize, + IN EAML_STREAM_DIRECTION Direction + ); + +/** Clone a stream. + + Cloning a stream means copying all the values of the input Stream + in the ClonedStream. + + @param [in] Stream Pointer to the stream to clone. + @param [in] ClonedStream Pointer to the stream to initialize. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlStreamClone ( + IN CONST AML_STREAM * Stream, + OUT AML_STREAM * ClonedStream + ); + +/** Initialize a sub-stream from a stream. + + A sub-stream is a stream initialized at the current position of the input + stream: + - the Buffer field points to the current position of the input stream; + - the Index field is set to 0; + - the MaxBufferSize field is set to the remaining size of the input stream; + - the direction is conserved; + + E.g.: For a forward stream: + +----------------+----------------+ + |ABCD.........XYZ| Free Space | + +----------------+----------------+ + ^ ^ ^ + Stream: Buffer CurrPos EndOfBuff + Sub-stream: Buffer/CurrPos EndOfBuff + + @param [in] Stream Pointer to the stream from which a sub-stream is + created. + @param [in] SubStream Pointer to the stream to initialize. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlStreamInitSubStream ( + IN CONST AML_STREAM * Stream, + OUT AML_STREAM * SubStream + ); + +/** Get the buffer of a stream. + + @param [in] Stream Pointer to a stream. + + @return The stream's Buffer. + NULL otherwise. +**/ +UINT8 * +EFIAPI +AmlStreamGetBuffer ( + IN CONST AML_STREAM * Stream + ); + +/** Get the size of Stream's Buffer. + + @param [in] Stream Pointer to a stream. + + @return The Size of Stream's Buffer. + Return 0 if Stream is invalid. +**/ +UINT32 +EFIAPI +AmlStreamGetMaxBufferSize ( + IN CONST AML_STREAM * Stream + ); + +/** Reduce the maximal size of Stream's Buffer (MaxBufferSize field). + + @param [in] Stream Pointer to a stream. + @param [in] Diff Value to subtract to the Stream's MaxBufferSize. + 0 < x < MaxBufferSize - Index. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlStreamReduceMaxBufferSize ( + IN AML_STREAM * Stream, + IN UINT32 Diff + ); + +/** Get Stream's Index. + + Stream's Index is incremented when writing data, reading data, + or moving the position in the Stream. + It can be seen as an index: + - starting at the beginning of Stream's Buffer if the stream goes forward; + - starting at the end of Stream's Buffer if the stream goes backward. + + @param [in] Stream Pointer to a stream. + + @return Stream's Index. + Return 0 if Stream is invalid. +**/ +UINT32 +EFIAPI +AmlStreamGetIndex ( + IN CONST AML_STREAM * Stream + ); + +/** Get Stream's Direction. + + @param [in] Stream Pointer to a stream. + + @return Stream's Direction. + Return EAmlStreamDirectionUnknown if Stream is invalid. +**/ +EAML_STREAM_DIRECTION +EFIAPI +AmlStreamGetDirection ( + IN CONST AML_STREAM * Stream + ); + +/** Return a pointer to the current position in the stream. + + @param [in] Stream Pointer to a stream. + + @return The current position in the stream. + Return NULL if error. +**/ +UINT8 * +EFIAPI +AmlStreamGetCurrPos ( + IN CONST AML_STREAM * Stream + ); + +/** Get the space available in the stream. + + @param [in] Stream Pointer to a stream. + + @return Remaining space available in the stream. + Zero in case of error or if the stream is at its end. +**/ +UINT32 +EFIAPI +AmlStreamGetFreeSpace ( + IN CONST AML_STREAM * Stream + ); + +/** Move Stream by Offset bytes. + + The stream current position is moved according to the stream direction + (forward, backward). + + @param [in] Stream Pointer to a stream. + The stream must not be at its end. + @param [in] Offset Offset to move the stream of. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. +**/ +EFI_STATUS +EFIAPI +AmlStreamProgress ( + IN AML_STREAM * Stream, + IN UINT32 Offset + ); + +/** Rewind Stream of Offset bytes. + + The stream current position is rewound according to the stream direction + (forward, backward). A stream going forward will be rewound backward. + + @param [in] Stream Pointer to a stream. + @param [in] Offset Offset to rewind the stream of. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. +**/ +EFI_STATUS +EFIAPI +AmlStreamRewind ( + IN AML_STREAM * Stream, + IN UINT32 Offset + ); + +/** Reset the Stream (move the current position to the initial position). + + @param [in] Stream Pointer to a stream. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlStreamReset ( + IN AML_STREAM * Stream + ); + +/** Peek one byte at Stream's current position. + + Stream's position is not moved when peeking. + + @param [in] Stream Pointer to a stream. + The stream must not be at its end. + @param [out] OutByte Pointer holding the byte value of + the stream current position. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. +**/ +EFI_STATUS +EFIAPI +AmlStreamPeekByte ( + IN AML_STREAM * Stream, + OUT UINT8 * OutByte + ); + +/** Read one byte at Stream's current position. + + The stream current position is moved when reading. + + @param [in] Stream Pointer to a stream. + The stream must not be at its end. + @param [out] OutByte Pointer holding the byte value of + the stream current position. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. +**/ +EFI_STATUS +EFIAPI +AmlStreamReadByte ( + IN AML_STREAM * Stream, + OUT UINT8 * OutByte + ); + +/** Write Size bytes in the stream. + + If the stream goes backward (toward lower addresses), the bytes written + to the stream are not reverted. + In the example below, writing "Hello" to the stream will not revert + the string. The end of the stream buffer will contain "Hello world!". + Stream buffer: + +---------------+-----+-----+-----+-----+-----+-----+---- +------+ + | ..... | ' ' | 'w' | 'o' | 'r' | 'l' | 'd' | '!' | '\0' | + +---------------+-----+-----+-----+-----+-----+-----+---- +------+ + ^ + Current position. + + @param [in] Stream Pointer to a stream. + The stream must not be at its end. + @param [in] Buffer Pointer to the data to write. + @param [in] Size Number of bytes to write. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. +**/ +EFI_STATUS +EFIAPI +AmlStreamWrite ( + IN AML_STREAM * Stream, + IN CONST UINT8 * Buffer, + IN UINT32 Size + ); + +/** Compare Size bytes between Stream1 and Stream2 from their + respective current position. + + Stream1 and Stream2 must go in the same direction. + Stream1 and Stream2 are left unchanged. + + @param [in] Stream1 First stream to compare. + The stream must not be at its end. + @param [in] Stream2 Second stream to compare. + The stream must not be at its end. + @param [in] Size Number of bytes to compare. + Must be lower than the minimum remaining space of + Stream1 and Stream2. + Must be non-zero. + + @retval TRUE If Stream1 and Stream2 have Size bytes equal, + from their respective current position. + The function completed successfully. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlStreamCmp ( + IN CONST AML_STREAM * Stream1, + IN CONST AML_STREAM * Stream2, + IN UINT32 Size + ); + +/** Copy Size bytes of the stream's data to DstBuffer. + + For a backward stream, the bytes are copied starting from the + current stream position. + + @param [out] DstBuffer Destination Buffer to copy the data to. + @param [in] MaxDstBufferSize Maximum size of DstBuffer. + Must be non-zero. + @param [in] Stream Pointer to the stream to copy the data from. + @param [in] Size Number of bytes to copy from the stream + buffer. + Must be lower than MaxDstBufferSize. + Must be lower than Stream's MaxBufferSize. + Return success if zero. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlStreamCpyS ( + OUT CHAR8 * DstBuffer, + IN UINT32 MaxDstBufferSize, + IN AML_STREAM * Stream, + IN UINT32 Size + ); + +#endif // AML_STREAM_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.c new file mode 100644 index 000000000..e9d36f4b6 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.c @@ -0,0 +1,1022 @@ +/** @file + AML String. + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include + +/** Check NameString/path information is valid. + + Root, ParentPrefix and SegCount cannot be 0 at the same time. + This function works for ASL and AML name strings. + + @param [in] Root Number of root char. + Must be 0 or 1. + @param [in] ParentPrefix Number of carets char ('^'). + Must be [0-255]. + @param [in] SegCount Number of NameSeg (s). + Must be [0-255]. + + @retval TRUE id the input information is in the right boundaries. + FALSE otherwise. +**/ +BOOLEAN +EFIAPI +AmlIsNameString ( + IN UINT32 Root, + IN UINT32 ParentPrefix, + IN UINT32 SegCount + ) +{ + if (((Root == 0) || (Root == 1)) && + (ParentPrefix <= MAX_UINT8) && + (!((ParentPrefix != 0) && (Root != 0))) && + (SegCount <= MAX_UINT8) && + ((SegCount + Root + ParentPrefix) != 0)) { + return TRUE; + } + return FALSE; +} + +/** Copy bytes from SrcBuffer to DstBuffer and convert to upper case. + Don't copy more than MaxDstBufferSize bytes. + + @param [out] DstBuffer Destination buffer. + @param [in] MaxDstBufferSize Maximum size of DstBuffer. + Must be non-zero. + @param [in] SrcBuffer Source buffer. + @param [in] Count Count of bytes to copy from SrcBuffer. + Return success if 0. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlUpperCaseMemCpyS ( + OUT CHAR8 * DstBuffer, + IN UINT32 MaxDstBufferSize, + IN CONST CHAR8 * SrcBuffer, + IN UINT32 Count + ) +{ + UINT32 Index; + + if ((DstBuffer == NULL) || + (SrcBuffer == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (Count == 0) { + return EFI_SUCCESS; + } + + if (Count > MaxDstBufferSize) { + Count = MaxDstBufferSize; + } + + for (Index = 0; Index < Count; Index++) { + if ((SrcBuffer[Index] >= 'a') && (SrcBuffer[Index] <= 'z')) { + DstBuffer[Index] = (CHAR8)((UINT8)SrcBuffer[Index] - ('a' - 'A')); + } else { + DstBuffer[Index] = SrcBuffer[Index]; + } + } + + return EFI_SUCCESS; +} + +/** Check whether Buffer is a root path ('\'). + + This function works for both ASL and AML pathnames. + Buffer must be at least 2 bytes long. + + @param [in] Buffer An ASL/AML path. + + @retval TRUE Buffer is a root path + @retval FALSE Buffer is not a root path. +**/ +BOOLEAN +EFIAPI +AmlIsRootPath ( + IN CONST CHAR8 * Buffer + ) +{ + if (Buffer == NULL) { + return FALSE; + } + + if ((Buffer[0] == AML_ROOT_CHAR) && (Buffer[1] == '\0')) { + return TRUE; + } else { + return FALSE; + } +} + +/** Check whether Ch is an ASL/AML LeadName. + + This function works for both ASL and AML pathnames. + + ACPI 6.3 specification, s19.2.2. "ASL Name and Pathname Terms": + LeadNameChar := 'A'-'Z' | 'a'-'z' | '_' + + ACPI 6.3 specification, s20.2.2. "Name Objects Encoding": + LeadNameChar := 'A'-'Z' | 'a'-'z' | '_' + + @param [in] Ch The char to test. + + @retval TRUE Ch is an ASL/AML LeadName. + @retval FALSE Ch is not an ASL/AML LeadName. +**/ +BOOLEAN +EFIAPI +AmlIsLeadNameChar ( + IN CHAR8 Ch + ) +{ + if ((Ch == '_') || (Ch >= 'A' && Ch <= 'Z') || (Ch >= 'a' && Ch <= 'z')) { + return TRUE; + } else { + return FALSE; + } +} + +/** Check whether Ch is an ASL/AML NameChar. + + This function works for both ASL and AML pathnames. + + ACPI 6.3 specification, s19.2.2. "ASL Name and Pathname Terms": + NameChar := DigitChar | LeadNameChar + LeadNameChar := 'A'-'Z' | 'a'-'z' | '_' + DigitChar := '0'-'9' + + ACPI 6.3 specification, s20.2.2. "Name Objects Encoding": + NameChar := DigitChar | LeadNameChar + LeadNameChar := 'A'-'Z' | 'a'-'z' | '_' + DigitChar := '0'-'9' + + @param [in] Ch The char to test. + + @retval TRUE Ch is an ASL/AML NameChar. + @retval FALSE Ch is not an ASL/AML NameChar. +**/ +BOOLEAN +EFIAPI +AmlIsNameChar ( + IN CHAR8 Ch + ) +{ + if (AmlIsLeadNameChar (Ch) || (Ch >= '0' && Ch <= '9')) { + return TRUE; + } else { + return FALSE; + } +} + +/** Check whether AslBuffer is an ASL NameSeg. + + This function only works for ASL NameStrings/pathnames. + ASL NameStrings/pathnames are at most 4 chars long. + + @param [in] AslBuffer Pointer in an ASL NameString/pathname. + @param [out] Size Size of the NameSeg. + + @retval TRUE AslBuffer is an ASL NameSeg. + @retval FALSE AslBuffer is not an ASL NameSeg. +**/ +BOOLEAN +EFIAPI +AslIsNameSeg ( + IN CONST CHAR8 * AslBuffer, + OUT UINT32 * Size + ) +{ + UINT32 Index; + + if ((AslBuffer == NULL) || + (Size == NULL)) { + return FALSE; + } + + if (!AmlIsLeadNameChar (AslBuffer[0])) { + return FALSE; + } + + for (Index = 1; Index < AML_NAME_SEG_SIZE; Index++) { + if ((AslBuffer[Index] == '.') || + (AslBuffer[Index] == '\0')) { + *Size = Index; + return TRUE; + } else if (!AmlIsNameChar (AslBuffer[Index])) { + return FALSE; + } + } + + *Size = Index; + return TRUE; +} + +/** Check whether AmlBuffer is an AML NameSeg. + + This function only works for AML NameStrings/pathnames. + AML NameStrings/pathnames must be 4 chars long. + + @param [in] AmlBuffer Pointer in an AML NameString/pathname. + + @retval TRUE AmlBuffer is an AML NameSeg. + @retval FALSE AmlBuffer is not an AML NameSeg. +**/ +BOOLEAN +EFIAPI +AmlIsNameSeg ( + IN CONST CHAR8 * AmlBuffer + ) +{ + UINT32 Index; + + if (AmlBuffer == NULL) { + return FALSE; + } + + if (!AmlIsLeadNameChar (AmlBuffer[0])) { + return FALSE; + } + + for (Index = 1; Index < AML_NAME_SEG_SIZE; Index++) { + if (!AmlIsNameChar (AmlBuffer[Index])) { + return FALSE; + } + } + + return TRUE; +} + +/** Parse an ASL NameString/path. + + An ASL NameString/path must be NULL terminated. + Information found in the ASL NameString/path is returned via pointers: + Root, ParentPrefix, SegCount. + + @param [in] Buffer ASL NameString/path. + @param [out] Root Pointer holding the number of root char. + Can be 0 or 1. + @param [out] ParentPrefix Pointer holding the number of carets char ('^'). + Can be [0-255]. + @param [out] SegCount Pointer holding the number of NameSeg (s). + Can be [0-255]. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AslParseNameStringInfo ( + IN CONST CHAR8 * Buffer, + OUT UINT32 * Root, + OUT UINT32 * ParentPrefix, + OUT UINT32 * SegCount + ) +{ + UINT32 NameSegSize; + + if ((Buffer == NULL) || + (Root == NULL) || + (ParentPrefix == NULL) || + (SegCount == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *Root = 0; + *ParentPrefix = 0; + *SegCount = 0; + + // Handle Root and ParentPrefix(s). + if (*Buffer == AML_ROOT_CHAR) { + *Root = 1; + Buffer++; + } else if (*Buffer == AML_PARENT_PREFIX_CHAR) { + do { + Buffer++; + (*ParentPrefix)++; + } while (*Buffer == AML_PARENT_PREFIX_CHAR); + } + + // Handle SegCount(s). + while (AslIsNameSeg (Buffer, &NameSegSize)) { + // Safety checks on NameSegSize. + if ((NameSegSize == 0) || (NameSegSize > AML_NAME_SEG_SIZE)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Increment the NameSeg count. + (*SegCount)++; + Buffer += NameSegSize; + + // Skip the '.' separator if present. + if (*Buffer == '.') { + Buffer++; + } + } // while + + // An ASL NameString/path must be NULL terminated. + if (*Buffer != '\0') { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (!AmlIsNameString (*Root, *ParentPrefix, *SegCount)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** Parse an AML NameString/path. + + It is possible to determine the size of an AML NameString/path just + by sight reading it. So no overflow can occur. + Information found in the AML NameString/path is returned via pointers: + Root, ParentPrefix, SegCount. + + @param [in] Buffer AML NameString/path. + @param [out] Root Pointer holding the number of root char. + Can be 0 or 1. + @param [out] ParentPrefix Pointer holding the number of carets char ('^'). + Can be [0-255]. + @param [out] SegCount Pointer holding the number of NameSeg(s). + Can be [0-255]. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlParseNameStringInfo ( + IN CONST CHAR8 * Buffer, + OUT UINT32 * Root, + OUT UINT32 * ParentPrefix, + OUT UINT32 * SegCount + ) +{ + if ((Buffer == NULL) || + (Root == NULL) || + (ParentPrefix == NULL) || + (SegCount == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *Root = 0; + *ParentPrefix = 0; + *SegCount = 0; + + // Handle Root and ParentPrefix(s). + if (*Buffer == AML_ROOT_CHAR) { + *Root = 1; + Buffer++; + } else if (*Buffer == AML_PARENT_PREFIX_CHAR) { + do { + Buffer++; + (*ParentPrefix)++; + } while (*Buffer == AML_PARENT_PREFIX_CHAR); + } + + // Handle SegCount(s). + if (*Buffer == AML_DUAL_NAME_PREFIX) { + *SegCount = 2; + } else if (*Buffer == AML_MULTI_NAME_PREFIX) { + *SegCount = *((UINT8*)(Buffer + 1)); + } else if (AmlIsNameSeg (Buffer)) { + *SegCount = 1; + } else if (*Buffer == AML_ZERO_OP) { + *SegCount = 0; + } else { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Safety checks on exit. + if (!AmlIsNameString (*Root, *ParentPrefix, *SegCount)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** Compute the ASL NameString/path size from NameString + information (Root, ParentPrefix, SegCount). + + @param [in] Root Number of root char. + Can be 0 or 1. + @param [in] ParentPrefix Number of carets char ('^'). + Can be [0-255]. + @param [in] SegCount Pointer holding the number of NameSeg(s). + Can be [0-255]. + + @return Size of the ASL NameString/path. +**/ +UINT32 +EFIAPI +AslComputeNameStringSize ( + IN UINT32 Root, + IN UINT32 ParentPrefix, + IN UINT32 SegCount + ) +{ + UINT32 TotalSize; + + if (!AmlIsNameString (Root, ParentPrefix, SegCount)) { + ASSERT (0); + return 0; + } + + // Root and ParentPrefix(s). + TotalSize = Root + ParentPrefix; + + // Add size required for NameSeg(s). + TotalSize += (SegCount * AML_NAME_SEG_SIZE); + + // Add size required for '.' separator(s). + TotalSize += (SegCount > 1) ? (SegCount - 1) : 0; + + // Add 1 byte for NULL termination '\0'. + TotalSize += 1; + + return TotalSize; +} + +/** Compute the AML NameString/path size from NameString + information (Root, ParentPrefix, SegCount). + + @param [in] Root Number of root char. + Can be 0 or 1. + @param [in] ParentPrefix Number of carets char ('^'). + Can be [0-255]. + @param [in] SegCount Pointer holding the number of NameSeg(s). + Can be [0-255]. + + @return Size of the AML NameString/path. +**/ +UINT32 +EFIAPI +AmlComputeNameStringSize ( + IN UINT32 Root, + IN UINT32 ParentPrefix, + IN UINT32 SegCount + ) +{ + UINT32 TotalSize; + + if (!AmlIsNameString (Root, ParentPrefix, SegCount)) { + ASSERT (0); + return 0; + } + + // Root and ParentPrefix(s). + TotalSize = Root + ParentPrefix; + + // If SegCount == 0, '\0' must end the AML NameString/path. + TotalSize += (SegCount == 0) ? 1 : (SegCount * AML_NAME_SEG_SIZE); + + // AML prefix. SegCount > 2 = MultiNamePrefix, SegCount = 2 DualNamePrefix. + TotalSize += (SegCount > 2) ? 2 : ((SegCount == 2) ? 1 : 0); + + return TotalSize; +} + +/** Get the ASL NameString/path size. + + @param [in] AslPath An ASL NameString/path. + @param [out] AslPathSizePtr Pointer holding the ASL NameString/path size. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AslGetNameStringSize ( + IN CONST CHAR8 * AslPath, + OUT UINT32 * AslPathSizePtr + ) +{ + if ((AslPath == NULL) || + (AslPathSizePtr == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *AslPathSizePtr = 0; + do { + (*AslPathSizePtr)++; + AslPath++; + } while (*AslPath != '\0'); + + return EFI_SUCCESS; +} + +/** Get the AML NameString/path size. + + @param [in] AmlPath An AML NameString/path. + @param [out] AmlPathSizePtr Pointer holding the AML NameString/path size. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetNameStringSize ( + IN CONST CHAR8 * AmlPath, + OUT UINT32 * AmlPathSizePtr + ) +{ + EFI_STATUS Status; + + UINT32 Root; + UINT32 ParentPrefix; + UINT32 SegCount; + + if ((AmlPath == NULL) || + (AmlPathSizePtr == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlParseNameStringInfo ( + AmlPath, + &Root, + &ParentPrefix, + &SegCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + *AmlPathSizePtr = AmlComputeNameStringSize (Root, ParentPrefix, SegCount); + if (*AmlPathSizePtr == 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + return Status; +} + +/** Convert an ASL NameString/path to an AML NameString/path. + The caller must free the memory allocated in this function + for AmlPath using FreePool (). + + @param [in] AslPath An ASL NameString/path. + @param [out] OutAmlPath Buffer containing the AML path. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +ConvertAslNameToAmlName ( + IN CONST CHAR8 * AslPath, + OUT CHAR8 ** OutAmlPath + ) +{ + EFI_STATUS Status; + + UINT32 Root; + UINT32 ParentPrefix; + UINT32 SegCount; + UINT32 TotalSize; + UINT32 NameSegSize; + + CONST CHAR8 * AslBuffer; + CHAR8 * AmlBuffer; + CHAR8 * AmlPath; + + if ((AslPath == NULL) || + (OutAmlPath == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // Analyze AslPath. AslPath is checked in the call. + Status = AslParseNameStringInfo (AslPath, &Root, &ParentPrefix, &SegCount); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Compute TotalSize. + TotalSize = AmlComputeNameStringSize (Root, ParentPrefix, SegCount); + if (TotalSize == 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Allocate memory. + AmlPath = AllocateZeroPool (TotalSize); + if (AmlPath == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + AmlBuffer = AmlPath; + AslBuffer = AslPath; + + // Handle Root and ParentPrefix(s). + if (Root == 1) { + *AmlBuffer = AML_ROOT_CHAR; + AmlBuffer++; + AslBuffer++; + } else if (ParentPrefix > 0) { + SetMem (AmlBuffer, ParentPrefix, AML_PARENT_PREFIX_CHAR); + AmlBuffer += ParentPrefix; + AslBuffer += ParentPrefix; + } + + // Handle prefix and SegCount(s). + if (SegCount > 2) { + *AmlBuffer = AML_MULTI_NAME_PREFIX; + AmlBuffer++; + *AmlBuffer = (UINT8)SegCount; + AmlBuffer++; + } else if (SegCount == 2) { + *AmlBuffer = AML_DUAL_NAME_PREFIX; + AmlBuffer++; + } + + if (SegCount != 0) { + // Write NameSeg(s). + while (1) { + SegCount--; + + // Get the NameSeg size. + if (!AslIsNameSeg (AslBuffer, &NameSegSize)) { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + // Convert to Upper case and copy. + Status = AmlUpperCaseMemCpyS ( + AmlBuffer, + TotalSize, + AslBuffer, + NameSegSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + // Complete the NameSeg with an underscore ('_') if shorter than 4 bytes. + SetMem ( + AmlBuffer + NameSegSize, + AML_NAME_SEG_SIZE - NameSegSize, + AML_NAME_CHAR__ + ); + + // Go to the next NameSeg. + AmlBuffer += AML_NAME_SEG_SIZE; + AslBuffer += NameSegSize; + + // Skip the '.' separator. + if (SegCount != 0) { + if (*AslBuffer == '.') { + AslBuffer++; + } else { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + } else { + // (SegCount == 0) + if (*AslBuffer == '\0') { + break; + } else { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + } + } // while + + } else { + // (SegCount == 0) + // '\0' needs to end the AML NameString/path. + *AmlBuffer = AML_ZERO_OP; + AmlBuffer++; + } + + // Safety checks on exit. + // Check that AmlPath has been filled with TotalSize bytes. + if ((SegCount != 0) || + (*AslBuffer != AML_ZERO_OP) || + (((UINT32)(AmlBuffer - AmlPath)) != TotalSize)) { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + *OutAmlPath = AmlPath; + return EFI_SUCCESS; + +error_handler: + FreePool (AmlPath); + return Status; +} + +/** Convert an AML NameString/path to an ASL NameString/path. + The caller must free the memory allocated in this function. + using FreePool (). + + @param [in] AmlPath An AML NameString/path. + @param [out] OutAslPath Buffer containing the ASL path. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +ConvertAmlNameToAslName ( + IN CONST CHAR8 * AmlPath, + OUT CHAR8 ** OutAslPath + ) +{ + EFI_STATUS Status; + + UINT32 Root; + UINT32 ParentPrefix; + UINT32 SegCount; + UINT32 TotalSize; + + CONST CHAR8 * AmlBuffer; + CHAR8 * AslBuffer; + CHAR8 * AslPath; + + if ((AmlPath == NULL) || + (OutAslPath == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Analyze AslPath. AmlPath is checked in the call. + Status = AmlParseNameStringInfo (AmlPath, &Root, &ParentPrefix, &SegCount); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Compute TotalSize. + TotalSize = AslComputeNameStringSize (Root, ParentPrefix, SegCount); + if (TotalSize == 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Allocate memory. + AslPath = AllocateZeroPool (TotalSize); + if (AslPath == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + AmlBuffer = AmlPath; + AslBuffer = AslPath; + + // Handle prefix and SegCount(s). + if (Root == 1) { + *AslBuffer = AML_ROOT_CHAR; + AslBuffer++; + AmlBuffer++; + } else if (ParentPrefix > 0) { + SetMem (AslBuffer, ParentPrefix, AML_PARENT_PREFIX_CHAR); + AslBuffer += ParentPrefix; + AmlBuffer += ParentPrefix; + } + + // Handle Root and Parent(s). + // Skip the MultiName or DualName prefix chars. + if (SegCount > 2) { + AmlBuffer += 2; + } else if (SegCount == 2) { + AmlBuffer += 1; + } + + // Write NameSeg(s). + while (SegCount) { + // NameSeg is already in upper case and always 4 bytes long. + CopyMem (AslBuffer, AmlBuffer, AML_NAME_SEG_SIZE); + AslBuffer += AML_NAME_SEG_SIZE; + AmlBuffer += AML_NAME_SEG_SIZE; + + SegCount--; + + // Write the '.' separator if there is another NameSeg following. + if (SegCount != 0) { + *AslBuffer = '.'; + AslBuffer++; + } + } // while + + // NULL terminate the ASL NameString. + *AslBuffer = '\0'; + AslBuffer++; + + // Safety checks on exit. + // Check that AslPath has been filled with TotalSize bytes. + if (((UINT32)(AslBuffer - AslPath)) != TotalSize) { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + *OutAslPath = AslPath; + return EFI_SUCCESS; + +error_handler: + FreePool (AslPath); + return Status; +} + +/** Compare two ASL NameStrings. + + @param [in] AslName1 First NameString to compare. + @param [in] AslName2 Second NameString to compare. + + @retval TRUE if the two strings are identical. + @retval FALSE otherwise, or if error. +**/ +BOOLEAN +EFIAPI +AslCompareNameString ( + IN CONST CHAR8 * AslName1, + IN CONST CHAR8 * AslName2 + ) +{ + EFI_STATUS Status; + UINT32 AslName1Len; + UINT32 AslName2Len; + + if ((AslName1 == NULL) || + (AslName2 == NULL)) { + ASSERT (0); + return FALSE; + } + + Status = AslGetNameStringSize (AslName1, &AslName1Len); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + Status = AslGetNameStringSize (AslName2, &AslName2Len); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // AslName1 and AslName2 don't have the same length + if (AslName1Len != AslName2Len) { + return FALSE; + } + + return (CompareMem (AslName1, AslName2, AslName1Len) == 0); +} + +/** Compare two AML NameStrings. + + @param [in] AmlName1 First NameString to compare. + @param [in] AmlName2 Second NameString to compare. + + @retval TRUE if the two strings are identical. + @retval FALSE otherwise, or if error. +**/ +BOOLEAN +EFIAPI +AmlCompareNameString ( + IN CONST CHAR8 * AmlName1, + IN CONST CHAR8 * AmlName2 + ) +{ + EFI_STATUS Status; + UINT32 AmlName1Len; + UINT32 AmlName2Len; + + if ((AmlName1 == NULL) || + (AmlName2 == NULL)) { + ASSERT (0); + return FALSE; + } + + Status = AmlGetNameStringSize (AmlName1, &AmlName1Len); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + Status = AmlGetNameStringSize (AmlName2, &AmlName2Len); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // AmlName1 and AmlName2 don't have the same length + if (AmlName1Len != AmlName2Len) { + return FALSE; + } + + return (CompareMem (AmlName1, AmlName2, AmlName1Len) == 0); +} + +/** Compare an AML NameString and an ASL NameString. + + The ASL NameString is converted to an AML NameString before + being compared with the ASL NameString. This allows to expand + NameSegs shorter than 4 chars. + E.g.: AslName: "DEV" will be expanded to "DEV_" before being + compared. + + @param [in] AmlName1 AML NameString to compare. + @param [in] AslName2 ASL NameString to compare. + + @retval TRUE if the two strings are identical. + @retval FALSE otherwise, or if error. +**/ +BOOLEAN +EFIAPI +CompareAmlWithAslNameString ( + IN CONST CHAR8 * AmlName1, + IN CONST CHAR8 * AslName2 + ) +{ + EFI_STATUS Status; + + CHAR8 * AmlName2; + BOOLEAN RetVal; + + if ((AmlName1 == NULL) || + (AslName2 == NULL)) { + ASSERT (0); + return FALSE; + } + + // Convert the AslName2 to an AmlName2. + // AmlName2 must be freed. + Status = ConvertAmlNameToAslName (AslName2, &AmlName2); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + RetVal = AmlCompareNameString (AmlName1, AmlName2); + + // Free AmlName2. + FreePool (AmlName2); + + return RetVal; +} +/** Given an AmlPath, return the address of the first NameSeg. + + It is possible to determine the size of an AML NameString/path just + by sight reading it. So no overflow can occur. + + @param [in] AmlPath The AML pathname. + @param [in] Root The AML pathname starts with a root char. + It is an absolute path. + @param [in] ParentPrefix The AML pathname has ParentPrefix + carets in its name. + + @return Pointer to the first NameSeg of the NameString. + Return NULL if AmlPath is NULL. +**/ +CONST +CHAR8 * +EFIAPI +AmlGetFirstNameSeg ( + IN CONST CHAR8 * AmlPath, + IN UINT32 Root, + IN UINT32 ParentPrefix + ) +{ + if (AmlPath == NULL) { + ASSERT (0); + return NULL; + } + + AmlPath += Root; + AmlPath += ParentPrefix; + AmlPath += ((*AmlPath == AML_MULTI_NAME_PREFIX) ? 2 + : (*AmlPath == AML_DUAL_NAME_PREFIX) ? 1 : 0); + return AmlPath; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.h new file mode 100644 index 000000000..86d9df5f1 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/String/AmlString.h @@ -0,0 +1,401 @@ +/** @file + AML String. + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_STRING_H_ +#define AML_STRING_H_ + +/* This header file does not include internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. The node definitions + must be included by the caller file. The function prototypes must + only expose AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, etc. node + definitions. + This allows to keep the functions defined here both internal and + potentially external. If necessary, any function of this file can + be exposed externally. + The Api folder is internal to the AmlLib, but should only use these + functions. They provide a "safe" way to interact with the AmlLib. +*/ + +#include + +/** Check NameString/path information is valid. + + Root, ParentPrefix and SegCount cannot be 0 at the same time. + This function works for ASL and AML name strings. + + @param [in] Root Number of root char. + Must be 0 or 1. + @param [in] ParentPrefix Number of carets char ('^'). + Must be [0-255]. + @param [in] SegCount Number of NameSeg (s). + Must be [0-255]. + + @retval TRUE id the input information is in the right boundaries. + FALSE otherwise. +**/ +BOOLEAN +EFIAPI +AmlIsNameString ( + IN UINT32 Root, + IN UINT32 ParentPrefix, + IN UINT32 SegCount + ); + +/** Copy bytes from SrcBuffer to DstBuffer and convert to upper case. + Don't copy more than MaxDstBufferSize bytes. + + @param [out] DstBuffer Destination buffer. + @param [in] MaxDstBufferSize Maximum size of DstBuffer. + Must be non-zero. + @param [in] SrcBuffer Source buffer. + @param [in] Count Count of bytes to copy from SrcBuffer. + Return success if 0. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlUpperCaseMemCpyS ( + OUT CHAR8 * DstBuffer, + IN UINT32 MaxDstBufferSize, + IN CONST CHAR8 * SrcBuffer, + IN UINT32 Count + ); + +/** Check whether Buffer is a root path ('\'). + + This function works for both ASL and AML pathnames. + Buffer must be at least 2 bytes long. + + @param [in] Buffer An ASL/AML path. + + @retval TRUE Buffer is a root path + @retval FALSE Buffer is not a root path. +**/ +BOOLEAN +EFIAPI +AmlIsRootPath ( + IN CONST CHAR8 * Buffer + ); + +/** Check whether Ch is an ASL/AML LeadName. + + This function works for both ASL and AML pathnames. + + ACPI 6.3 specification, s19.2.2. "ASL Name and Pathname Terms": + LeadNameChar := 'A'-'Z' | 'a'-'z' | '_' + + ACPI 6.3 specification, s20.2.2. "Name Objects Encoding": + LeadNameChar := 'A'-'Z' | 'a'-'z' | '_' + + @param [in] Ch The char to test. + + @retval TRUE Ch is an ASL/AML LeadName. + @retval FALSE Ch is not an ASL/AML LeadName. +**/ +BOOLEAN +EFIAPI +AmlIsLeadNameChar ( + IN CHAR8 Ch + ); + +/** Check whether Ch is an ASL/AML NameChar. + + This function works for both ASL and AML pathnames. + + ACPI 6.3 specification, s19.2.2. "ASL Name and Pathname Terms": + NameChar := DigitChar | LeadNameChar + LeadNameChar := 'A'-'Z' | 'a'-'z' | '_' + DigitChar := '0'-'9' + + ACPI 6.3 specification, s20.2.2. "Name Objects Encoding": + NameChar := DigitChar | LeadNameChar + LeadNameChar := 'A'-'Z' | 'a'-'z' | '_' + DigitChar := '0'-'9' + + @param [in] Ch The char to test. + + @retval TRUE Ch is an ASL/AML NameChar. + @retval FALSE Ch is not an ASL/AML NameChar. +**/ +BOOLEAN +EFIAPI +AmlIsNameChar ( + IN CHAR8 Ch + ); + +/** Check whether AslBuffer is an ASL NameSeg. + + This function only works for ASL NameStrings/pathnames. + ASL NameStrings/pathnames are at most 4 chars long. + + @param [in] AslBuffer Pointer in an ASL NameString/pathname. + @param [out] Size Size of the NameSeg. + + @retval TRUE AslBuffer is an ASL NameSeg. + @retval FALSE AslBuffer is not an ASL NameSeg. +**/ +BOOLEAN +EFIAPI +AslIsNameSeg ( + IN CONST CHAR8 * AslBuffer, + OUT UINT32 * Size + ); + +/** Check whether AmlBuffer is an AML NameSeg. + + This function only works for AML NameStrings/pathnames. + AML NameStrings/pathnames must be 4 chars long. + + @param [in] AmlBuffer Pointer in an AML NameString/pathname. + + @retval TRUE AmlBuffer is an AML NameSeg. + @retval FALSE AmlBuffer is not an AML NameSeg. +**/ +BOOLEAN +EFIAPI +AmlIsNameSeg ( + IN CONST CHAR8 * AmlBuffer + ); + +/** Parse an ASL NameString/path. + + An ASL NameString/path must be NULL terminated. + Information found in the ASL NameString/path is returned via pointers: + Root, ParentPrefix, SegCount. + + @param [in] Buffer ASL NameString/path. + @param [out] Root Pointer holding the number of root char. + Can be 0 or 1. + @param [out] ParentPrefix Pointer holding the number of carets char ('^'). + Can be [0-255]. + @param [out] SegCount Pointer holding the number of NameSeg (s). + Can be [0-255]. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AslParseNameStringInfo ( + IN CONST CHAR8 * Buffer, + OUT UINT32 * Root, + OUT UINT32 * ParentPrefix, + OUT UINT32 * SegCount + ); + +/** Parse an AML NameString/path. + + It is possible to determine the size of an AML NameString/path just + by sight reading it. So no overflow can occur. + Information found in the AML NameString/path is returned via pointers: + Root, ParentPrefix, SegCount. + + @param [in] Buffer AML NameString/path. + @param [out] Root Pointer holding the number of root char. + Can be 0 or 1. + @param [out] ParentPrefix Pointer holding the number of carets char ('^'). + Can be [0-255]. + @param [out] SegCount Pointer holding the number of NameSeg(s). + Can be [0-255]. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlParseNameStringInfo ( + IN CONST CHAR8 * Buffer, + OUT UINT32 * Root, + OUT UINT32 * ParentPrefix, + OUT UINT32 * SegCount + ); + +/** Compute the ASL NameString/path size from NameString + information (Root, ParentPrefix, SegCount). + + @param [in] Root Number of root char. + Can be 0 or 1. + @param [in] ParentPrefix Number of carets char ('^'). + Can be [0-255]. + @param [in] SegCount Pointer holding the number of NameSeg(s). + Can be [0-255]. + + @return Size of the ASL NameString/path. +**/ +UINT32 +EFIAPI +AslComputeNameStringSize ( + IN UINT32 Root, + IN UINT32 ParentPrefix, + IN UINT32 SegCount + ); + +/** Compute the AML NameString/path size from NameString + information (Root, ParentPrefix, SegCount). + + @param [in] Root Number of root char. + Can be 0 or 1. + @param [in] ParentPrefix Number of carets char ('^'). + Can be [0-255]. + @param [in] SegCount Pointer holding the number of NameSeg(s). + Can be [0-255]. + + @return Size of the AML NameString/path. +**/ +UINT32 +EFIAPI +AmlComputeNameStringSize ( + IN UINT32 Root, + IN UINT32 ParentPrefix, + IN UINT32 SegCount + ); + +/** Get the ASL NameString/path size. + + @param [in] AslPath An ASL NameString/path. + @param [out] AslPathSizePtr Pointer holding the ASL NameString/path size. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AslGetNameStringSize ( + IN CONST CHAR8 * AslPath, + OUT UINT32 * AslPathSizePtr + ); + +/** Get the AML NameString/path size. + + @param [in] AmlPath An AML NameString/path. + @param [out] AmlPathSizePtr Pointer holding the AML NameString/path size. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetNameStringSize ( + IN CONST CHAR8 * AmlPath, + OUT UINT32 * AmlPathSizePtr + ); + +/** Convert an ASL NameString/path to an AML NameString/path. + The caller must free the memory allocated in this function + for AmlPath using FreePool (). + + @param [in] AslPath An ASL NameString/path. + @param [out] OutAmlPath Buffer containing the AML path. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +ConvertAslNameToAmlName ( + IN CONST CHAR8 * AslPath, + OUT CHAR8 ** OutAmlPath + ); + +/** Convert an AML NameString/path to an ASL NameString/path. + The caller must free the memory allocated in this function. + using FreePool (). + + @param [in] AmlPath An AML NameString/path. + @param [out] OutAslPath Buffer containing the ASL path. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +ConvertAmlNameToAslName ( + IN CONST CHAR8 * AmlPath, + OUT CHAR8 ** OutAslPath + ); + +/** Compare two ASL NameStrings. + + @param [in] AslName1 First NameString to compare. + @param [in] AslName2 Second NameString to compare. + + @retval TRUE if the two strings are identical. + @retval FALSE otherwise, or if error. +**/ +BOOLEAN +EFIAPI +AslCompareNameString ( + IN CONST CHAR8 * AslName1, + IN CONST CHAR8 * AslName2 + ); + +/** Compare two AML NameStrings. + + @param [in] AmlName1 First NameString to compare. + @param [in] AmlName2 Second NameString to compare. + + @retval TRUE if the two strings are identical. + @retval FALSE otherwise, or if error. +**/ +BOOLEAN +EFIAPI +AmlCompareNameString ( + IN CONST CHAR8 * AmlName1, + IN CONST CHAR8 * AmlName2 + ); + +/** Compare an AML NameString and an ASL NameString. + + The ASL NameString is converted to an AML NameString before + being compared with the ASL NameString. This allows to expand + NameSegs shorter than 4 chars. + E.g.: AslName: "DEV" will be expanded to "DEV_" before being + compared. + + @param [in] AmlName1 AML NameString to compare. + @param [in] AslName2 ASL NameString to compare. + + @retval TRUE if the two strings are identical. + @retval FALSE otherwise, or if error. +**/ +BOOLEAN +EFIAPI +CompareAmlWithAslNameString ( + IN CONST CHAR8 * AmlName1, + IN CONST CHAR8 * AslName2 + ); + +/** Given an AmlPath, return the address of the first NameSeg. + + It is possible to determine the size of an AML NameString/path just + by sight reading it. So no overflow can occur. + + @param [in] AmlPath The AML pathname. + @param [in] Root The AML pathname starts with a root char. + It is an absolute path. + @param [in] ParentPrefix The AML pathname has ParentPrefix + carets in its name. + + @return Pointer to the first NameSeg of the NameString. + Return NULL if AmlPath is NULL. +**/ +CONST +CHAR8 * +EFIAPI +AmlGetFirstNameSeg ( + IN CONST CHAR8 * AmlPath, + IN UINT32 Root, + IN UINT32 ParentPrefix + ); + +#endif // AML_STRING_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c new file mode 100644 index 000000000..e09372b03 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c @@ -0,0 +1,205 @@ +/** @file + AML Clone. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include + +/** Clone a node. + + This function does not clone the children nodes. + The cloned node returned is not attached to any tree. + + @param [in] Node Pointer to a node. + @param [out] ClonedNode Pointer holding the cloned 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 +AmlCloneNode ( + IN AML_NODE_HEADER * Node, + OUT AML_NODE_HEADER ** ClonedNode + ) +{ + EFI_STATUS Status; + + AML_OBJECT_NODE * ObjectNode; + AML_DATA_NODE * DataNode; + AML_ROOT_NODE * RootNode; + + if (!IS_AML_NODE_VALID (Node) || + (ClonedNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *ClonedNode = NULL; + + if (IS_AML_DATA_NODE (Node)) { + DataNode = (AML_DATA_NODE*)Node; + Status = AmlCreateDataNode ( + DataNode->DataType, + DataNode->Buffer, + DataNode->Size, + (AML_DATA_NODE**)ClonedNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + } else if (IS_AML_OBJECT_NODE (Node)) { + ObjectNode = (AML_OBJECT_NODE*)Node; + + Status = AmlCreateObjectNode ( + ObjectNode->AmlByteEncoding, + ObjectNode->PkgLen, + (AML_OBJECT_NODE**)ClonedNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + } else if (IS_AML_ROOT_NODE (Node)) { + RootNode = (AML_ROOT_NODE*)Node; + + Status = AmlCreateRootNode ( + RootNode->SdtHeader, + (AML_ROOT_NODE**)ClonedNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + } else { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + return Status; +} + +/** Clone a node and its children (clone a tree branch). + + The cloned branch returned is not attached to any tree. + + @param [in] Node Pointer to a node. + Node is the head of the branch to clone. + @param [out] ClonedNode Pointer holding the head of the created cloned + branch. + + @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 +AmlCloneTree ( + IN AML_NODE_HEADER * Node, + OUT AML_NODE_HEADER ** ClonedNode + ) +{ + EFI_STATUS Status; + + AML_NODE_HEADER * HeadNode; + AML_NODE_HEADER * ClonedChildNode; + AML_NODE_HEADER * FixedArgNode; + + EAML_PARSE_INDEX Index; + EAML_PARSE_INDEX MaxIndex; + + LIST_ENTRY * StartLink; + LIST_ENTRY * CurrentLink; + + if (!IS_AML_NODE_VALID (Node) || + (ClonedNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlCloneNode (Node, &HeadNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Clone the fixed arguments and bind them to their parent. + MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount ( + (AML_OBJECT_NODE*)Node + ); + for (Index = EAmlParseIndexTerm0; Index < MaxIndex; Index++) { + FixedArgNode = AmlGetFixedArgument ((AML_OBJECT_NODE*)Node, Index); + if (FixedArgNode == NULL) { + Status = EFI_INVALID_PARAMETER; + ASSERT (0); + goto error_handler; + } + + // Clone child. + Status = AmlCloneTree ( + FixedArgNode, + &ClonedChildNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + // Bind child. + Status = AmlSetFixedArgument ( + (AML_OBJECT_NODE*)HeadNode, + Index, + ClonedChildNode + ); + if (EFI_ERROR (Status)) { + AmlDeleteTree (ClonedChildNode); + ASSERT (0); + goto error_handler; + } + } // for + + // Clone the variable arguments and bind them to their parent. + StartLink = AmlNodeGetVariableArgList (Node); + if (StartLink != NULL) { + CurrentLink = StartLink->ForwardLink; + while (CurrentLink != StartLink) { + // Clone child. + Status = AmlCloneTree ((AML_NODE_HEADER*)CurrentLink, &ClonedChildNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + // Bind child. + Status = AmlVarListAddTailInternal ( + HeadNode, + ClonedChildNode + ); + if (EFI_ERROR (Status)) { + AmlDeleteTree (ClonedChildNode); + ASSERT (0); + goto error_handler; + } + + CurrentLink = CurrentLink->ForwardLink; + } // while + } + + *ClonedNode = HeadNode; + return Status; + +error_handler: + *ClonedNode = NULL; + + if (HeadNode != NULL) { + AmlDeleteTree (HeadNode); + } + + return Status; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c new file mode 100644 index 000000000..2c80440a4 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c @@ -0,0 +1,673 @@ +/** @file + AML Node. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include + +/** Initialize an AML_NODE_HEADER structure. + + @param [in] Node Pointer to a node header. + @param [in] NodeType NodeType to initialize the Node with. + Must be an EAML_NODE_TYPE. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlInitializeNodeHeader ( + IN AML_NODE_HEADER * Node, + IN EAML_NODE_TYPE NodeType + ) +{ + if (Node == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + InitializeListHead (&Node->Link); + + Node->Parent = NULL; + Node->NodeType = NodeType; + + return EFI_SUCCESS; +} + +/** Delete a root node and its ACPI DSDT/SSDT header. + + It is the caller's responsibility to check the RootNode has been removed + from the tree and is not referencing any other node in the tree. + + @param [in] RootNode Pointer to a root node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlDeleteRootNode ( + IN AML_ROOT_NODE * RootNode + ) +{ + if (!IS_AML_ROOT_NODE (RootNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if ((RootNode->SdtHeader != NULL)) { + FreePool (RootNode->SdtHeader); + } else { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + FreePool (RootNode); + return EFI_SUCCESS; +} + +/** Create an AML_ROOT_NODE. + This node will be the root of the tree. + + @param [in] SdtHeader Pointer to an ACPI DSDT/SSDT header to copy + the data from. + @param [out] NewRootNodePtr The created AML_ROOT_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 +AmlCreateRootNode ( + IN CONST EFI_ACPI_DESCRIPTION_HEADER * SdtHeader, + OUT AML_ROOT_NODE ** NewRootNodePtr + ) +{ + EFI_STATUS Status; + AML_ROOT_NODE * RootNode; + + if ((SdtHeader == NULL) || + (NewRootNodePtr == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + RootNode = AllocateZeroPool (sizeof (AML_ROOT_NODE)); + if (RootNode == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + Status = AmlInitializeNodeHeader (&RootNode->NodeHeader, EAmlNodeRoot); + if (EFI_ERROR (Status)) { + FreePool (RootNode); + ASSERT (0); + return Status; + } + + InitializeListHead (&RootNode->VariableArgs); + + RootNode->SdtHeader = AllocateCopyPool ( + sizeof (EFI_ACPI_DESCRIPTION_HEADER), + SdtHeader + ); + if (RootNode->SdtHeader == NULL) { + ASSERT (0); + AmlDeleteRootNode (RootNode); + return EFI_OUT_OF_RESOURCES; + } + + *NewRootNodePtr = RootNode; + + return EFI_SUCCESS; +} + +/** Delete an object node. + + It is the caller's responsibility to check the ObjectNode has been removed + from the tree and is not referencing any other node in the tree. + + @param [in] ObjectNode Pointer to an object node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlDeleteObjectNode ( + IN AML_OBJECT_NODE * ObjectNode + ) +{ + if (!IS_AML_OBJECT_NODE (ObjectNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + FreePool (ObjectNode); + return EFI_SUCCESS; +} + +/** Create an AML_OBJECT_NODE. + + @param [in] AmlByteEncoding Byte encoding entry. + @param [in] PkgLength PkgLength of the node if the AmlByteEncoding + has the PkgLen attribute. + 0 otherwise. + @param [out] NewObjectNodePtr The created AML_OBJECT_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 +AmlCreateObjectNode ( + IN CONST AML_BYTE_ENCODING * AmlByteEncoding, + IN UINT32 PkgLength, + OUT AML_OBJECT_NODE ** NewObjectNodePtr + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE * ObjectNode; + + if ((AmlByteEncoding == NULL) || + (NewObjectNodePtr == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ObjectNode = AllocateZeroPool (sizeof (AML_OBJECT_NODE)); + if (ObjectNode == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + Status = AmlInitializeNodeHeader (&ObjectNode->NodeHeader, EAmlNodeObject); + if (EFI_ERROR (Status)) { + FreePool (ObjectNode); + ASSERT (0); + return Status; + } + + InitializeListHead (&ObjectNode->VariableArgs); + + // ObjectNode->FixedArgs[...] is already initialised to NULL as the + // ObjectNode is Zero allocated. + ObjectNode->AmlByteEncoding = AmlByteEncoding; + ObjectNode->PkgLen = PkgLength; + + *NewObjectNodePtr = ObjectNode; + + return EFI_SUCCESS; +} + +/** Delete a data node and its buffer. + + It is the caller's responsibility to check the DataNode has been removed + from the tree and is not referencing any other node in the tree. + + @param [in] DataNode Pointer to a data 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 +AmlDeleteDataNode ( + IN AML_DATA_NODE * DataNode + ) +{ + if (!IS_AML_DATA_NODE (DataNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (DataNode->Buffer != NULL) { + FreePool (DataNode->Buffer); + } else { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + FreePool (DataNode); + return EFI_SUCCESS; +} + +/** Create an AML_DATA_NODE. + + @param [in] DataType DataType of the node. + @param [in] Data Pointer to the AML bytecode corresponding to + this node. Data is copied from there. + @param [in] DataSize Number of bytes to consider at the address + pointed by Data. + @param [out] NewDataNodePtr The created AML_DATA_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 +AmlCreateDataNode ( + IN EAML_NODE_DATA_TYPE DataType, + IN CONST UINT8 * Data, + IN UINT32 DataSize, + OUT AML_DATA_NODE ** NewDataNodePtr + ) +{ + EFI_STATUS Status; + AML_DATA_NODE * DataNode; + + // A data node must not be created for certain data types. + if ((DataType == EAmlNodeDataTypeNone) || + (DataType == EAmlNodeDataTypeReserved1) || + (DataType == EAmlNodeDataTypeReserved2) || + (DataType == EAmlNodeDataTypeReserved3) || + (DataType == EAmlNodeDataTypeReserved4) || + (DataType == EAmlNodeDataTypeReserved5) || + (Data == NULL) || + (DataSize == 0) || + (NewDataNodePtr == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + DataNode = AllocateZeroPool (sizeof (AML_DATA_NODE)); + if (DataNode == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + Status = AmlInitializeNodeHeader (&DataNode->NodeHeader, EAmlNodeData); + if (EFI_ERROR (Status)) { + FreePool (DataNode); + ASSERT (0); + return Status; + } + + DataNode->Buffer = AllocateCopyPool (DataSize, Data); + if (DataNode->Buffer == NULL) { + AmlDeleteDataNode (DataNode); + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + DataNode->DataType = DataType; + DataNode->Size = DataSize; + + *NewDataNodePtr = DataNode; + + return EFI_SUCCESS; +} + +/** Delete a Node. + + @param [in] Node Pointer to a Node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeleteNode ( + IN AML_NODE_HEADER * Node + ) +{ + EFI_STATUS Status; + EAML_PARSE_INDEX Index; + + // Check that the node being deleted is unlinked. + // When removing the node, its parent and list are reset + // with InitializeListHead. Thus it must be empty. + if (!IS_AML_NODE_VALID (Node) || + !AML_NODE_IS_DETACHED (Node)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + switch (Node->NodeType) { + case EAmlNodeRoot: + { + // Check the variable list of arguments has been cleaned. + if (!IsListEmpty (AmlNodeGetVariableArgList (Node))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlDeleteRootNode ((AML_ROOT_NODE*)Node); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + break; + } + + case EAmlNodeObject: + { + // Check the variable list of arguments has been cleaned. + if (!IsListEmpty (AmlNodeGetVariableArgList (Node))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Check the fixed argument list has been cleaned. + for (Index = EAmlParseIndexTerm0; Index < EAmlParseIndexMax; Index++) { + if (((AML_OBJECT_NODE*)Node)->FixedArgs[Index] != NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } + + Status = AmlDeleteObjectNode ((AML_OBJECT_NODE*)Node); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + break; + } + + case EAmlNodeData: + { + Status = AmlDeleteDataNode ((AML_DATA_NODE*)Node); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + break; + } + + default: + { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + break; + } + } // switch + + return Status; +} + +/** Check whether ObjectNode has the input attribute. + This function can be used to check ObjectNode is an object node + at the same time. + + @param [in] ObjectNode Pointer to an object node. + @param [in] Attribute Attribute to check for. + + @retval TRUE The node is an AML object and the attribute is present. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasAttribute ( + IN CONST AML_OBJECT_NODE * ObjectNode, + IN AML_OP_ATTRIBUTE Attribute + ) +{ + if (!IS_AML_OBJECT_NODE (ObjectNode) || + (ObjectNode->AmlByteEncoding == NULL)) { + return FALSE; + } + + return ((ObjectNode->AmlByteEncoding->Attribute & + Attribute) == 0 ? FALSE : TRUE); +} + +/** Check whether ObjectNode has the input OpCode/SubOpcode couple. + + @param [in] ObjectNode Pointer to an object node. + @param [in] OpCode OpCode to check + @param [in] SubOpCode SubOpCode to check + + @retval TRUE The node is an AML object and + the Opcode and the SubOpCode match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeCompareOpCode ( + IN CONST AML_OBJECT_NODE * ObjectNode, + IN UINT8 OpCode, + IN UINT8 SubOpCode + ) +{ + if (!IS_AML_OBJECT_NODE (ObjectNode) || + (ObjectNode->AmlByteEncoding == NULL)) { + return FALSE; + } + + ASSERT (AmlIsOpCodeValid (OpCode, SubOpCode)); + + return ((ObjectNode->AmlByteEncoding->OpCode == OpCode) && + (ObjectNode->AmlByteEncoding->SubOpCode == SubOpCode)) ? + TRUE : FALSE; +} + +/** Check whether a Node is an integer node. + + By integer node we mean an object node having one of the following opcode: + - AML_BYTE_PREFIX; + - AML_WORD_PREFIX; + - AML_DWORD_PREFIX; + - AML_QWORD_PREFIX. + + @param [in] Node The node to check. + + @retval TRUE The Node is an integer node. + @retval FALSE Otherwise. +*/ +BOOLEAN +EFIAPI +IsIntegerNode ( + IN AML_OBJECT_NODE * Node + ) +{ + UINT8 OpCode; + + if (!IS_AML_OBJECT_NODE (Node) || + (Node->AmlByteEncoding == NULL)) { + return FALSE; + } + + // Check Node is an integer node. + OpCode = Node->AmlByteEncoding->OpCode; + if ((OpCode != AML_BYTE_PREFIX) && + (OpCode != AML_WORD_PREFIX) && + (OpCode != AML_DWORD_PREFIX) && + (OpCode != AML_QWORD_PREFIX)) { + return FALSE; + } + + return TRUE; +} + +/** Check whether a Node is a ZeroOp, a OneOp or a OnesOp. + + These two objects don't have a data node holding + a value. This require special handling. + + @param [in] Node The node to check. + + @retval TRUE The Node is a ZeroOp or OneOp. + @retval FALSE Otherwise. +*/ +BOOLEAN +EFIAPI +IsSpecialIntegerNode ( + IN AML_OBJECT_NODE * Node + ) +{ + UINT8 OpCode; + + if (!IS_AML_OBJECT_NODE (Node) || + (Node->AmlByteEncoding == NULL)) { + return FALSE; + } + + OpCode = Node->AmlByteEncoding->OpCode; + + if ((OpCode != AML_ZERO_OP) && + (OpCode != AML_ONE_OP) && + (OpCode != AML_ONES_OP)) { + return FALSE; + } + + return TRUE; +} + +/** Check whether Node corresponds to a method definition. + + A method definition can be introduced: + - By a method object, having an AML_METHOD_OP OpCode; + - By an external definition of a method, having an AML_EXTERNAL_OP OpCode + and an ObjectType byte set to the MethodObj. + + Note: + An alias node, having an AML_ALIAS_OP, can be resolved to a method + definition. This function doesn't handle this case. + + @param [in] Node Node to check whether it is a method definition. + + @retval TRUE The Node is a method definition. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlIsMethodDefinitionNode ( + IN CONST AML_OBJECT_NODE * Node + ) +{ + AML_DATA_NODE * ObjectType; + + // Node is checked to be an object node aswell. + if (AmlNodeCompareOpCode (Node, AML_METHOD_OP, 0)) { + return TRUE; + } else if (AmlNodeCompareOpCode (Node, AML_EXTERNAL_OP, 0)) { + // If the node is an external definition, check this is a method. + // DefExternal := ExternalOp NameString ObjectType ArgumentCount + // ExternalOp := 0x15 + // ObjectType := ByteData + // ArgumentCount := ByteData (0 – 7) + ObjectType = (AML_DATA_NODE*)AmlGetFixedArgument ( + (AML_OBJECT_NODE*)Node, + EAmlParseIndexTerm1 + ); + if (IS_AML_DATA_NODE (ObjectType) && + (ObjectType->DataType == EAmlNodeDataTypeUInt) && + ((ObjectType->Size == 1))) { + if (*((UINT8*)ObjectType->Buffer) == (UINT8)EAmlObjTypeMethodObj) { + // The external definition is a method. + return TRUE; + } else { + // The external definition is not a method. + return FALSE; + } + } else { + // The tree is inconsistent. + ASSERT (0); + return FALSE; + } + } + + // This is not a method definition. + return FALSE; +} + +/** Get the index at which the name of the node is stored. + + @param [in] ObjectNode Pointer to an object node. + Must have the AML_IN_NAMESPACE attribute. + @param [out] Index Index of the name in the fixed list of arguments. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +AmlNodeGetNameIndex ( + IN CONST AML_OBJECT_NODE * ObjectNode, + OUT EAML_PARSE_INDEX * Index + ) +{ + EAML_PARSE_INDEX NameIndex; + + if (!AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE) || + (ObjectNode->AmlByteEncoding == NULL) || + (Index == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + NameIndex = ObjectNode->AmlByteEncoding->NameIndex; + + if ((NameIndex > ObjectNode->AmlByteEncoding->MaxIndex) || + (ObjectNode->AmlByteEncoding->Format[NameIndex] != EAmlName)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *Index = NameIndex; + + return EFI_SUCCESS; +} + +/** Get the name of the Node. + + Node must be part of the namespace. + + @param [in] ObjectNode Pointer to an object node, + which is part of the namespace. + + @return A pointer to the name. + NULL otherwise. + Return NULL for the root node. +**/ +CHAR8 * +EFIAPI +AmlNodeGetName ( + IN CONST AML_OBJECT_NODE * ObjectNode + ) +{ + EFI_STATUS Status; + EAML_PARSE_INDEX NameIndex; + AML_DATA_NODE * DataNode; + + if (!AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE)) { + ASSERT (0); + return NULL; + } + + // Get the index at which the name is stored in the fixed arguments list. + Status = AmlNodeGetNameIndex (ObjectNode, &NameIndex); + if (EFI_ERROR (Status)) { + ASSERT (0); + return NULL; + } + + // The name is stored in a Data node. + DataNode = (AML_DATA_NODE*)ObjectNode->FixedArgs[NameIndex]; + if (IS_AML_DATA_NODE (DataNode) && + (DataNode->DataType == EAmlNodeDataTypeNameString)) { + return (CHAR8*)DataNode->Buffer; + } + + /* Return NULL if no name is found. + This can occur if the name of a node is defined as a further + fixed argument. + E.g.: CreateField (BD03, 0x28, Add (ID03 + 0x08), BF33) + ^ + The parser is here. + The parent of the Add statement is the CreateField statement. This + statement defines a name in the AML namespace. This name defined as + the fourth fixed argument. It hasn't been parsed yet. + */ + return NULL; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h new file mode 100644 index 000000000..3584b572b --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h @@ -0,0 +1,212 @@ +/** @file + AML Node. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_NODE_H_ +#define AML_NODE_H_ + +#include +#include + +/** Create an AML_ROOT_NODE. + This node will be the root of the tree. + + @param [in] SdtHeader Pointer to an ACPI DSDT/SSDT header to copy + the data from. + @param [out] NewRootNodePtr The created AML_ROOT_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 +AmlCreateRootNode ( + IN CONST EFI_ACPI_DESCRIPTION_HEADER * SdtHeader, + OUT AML_ROOT_NODE ** NewRootNodePtr + ); + +/** Create an AML_OBJECT_NODE. + + @param [in] AmlByteEncoding Byte encoding entry. + @param [in] PkgLength PkgLength of the node if the AmlByteEncoding + has the PkgLen attribute. + 0 otherwise. + @param [out] NewObjectNodePtr The created AML_OBJECT_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 +AmlCreateObjectNode ( + IN CONST AML_BYTE_ENCODING * AmlByteEncoding, + IN UINT32 PkgLength, + OUT AML_OBJECT_NODE ** NewObjectNodePtr + ); + +/** Create an AML_DATA_NODE. + + @param [in] DataType DataType of the node. + @param [in] Data Pointer to the AML bytecode corresponding to + this node. Data is copied from there. + @param [in] DataSize Number of bytes to consider at the address + pointed by Data. + @param [out] NewDataNodePtr The created AML_DATA_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 +AmlCreateDataNode ( + IN EAML_NODE_DATA_TYPE DataType, + IN CONST UINT8 * Data, + IN UINT32 DataSize, + OUT AML_DATA_NODE ** NewDataNodePtr + ); + +/** Delete a Node. + + @param [in] Node Pointer to a Node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeleteNode ( + IN AML_NODE_HEADER * Node + ); + +/** Check whether ObjectNode has the input attribute. + This function can be used to check ObjectNode is an object node + at the same time. + + @param [in] ObjectNode Pointer to an object node. + @param [in] Attribute Attribute to check for. + + @retval TRUE The node is an AML object and the attribute is present. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasAttribute ( + IN CONST AML_OBJECT_NODE * ObjectNode, + IN AML_OP_ATTRIBUTE Attribute + ); + +/** Check whether ObjectNode has the input OpCode/SubOpcode couple. + + @param [in] ObjectNode Pointer to an object node. + @param [in] OpCode OpCode to check + @param [in] SubOpCode SubOpCode to check + + @retval TRUE The node is an AML object and + the Opcode and the SubOpCode match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeCompareOpCode ( + IN CONST AML_OBJECT_NODE * ObjectNode, + IN UINT8 OpCode, + IN UINT8 SubOpCode + ); + +/** Check whether a Node is an integer node. + + By integer node we mean an object node having one of the following opcode: + - AML_BYTE_PREFIX; + - AML_WORD_PREFIX; + - AML_DWORD_PREFIX; + - AML_QWORD_PREFIX. + + @param [in] Node The node to check. + + @retval TRUE The Node is an integer node. + @retval FALSE Otherwise. +*/ +BOOLEAN +EFIAPI +IsIntegerNode ( + IN AML_OBJECT_NODE * Node + ); + +/** Check whether a Node is a ZeroOp, a OneOp or a OnesOp. + + These two objects don't have a data node holding + a value. This require special handling. + + @param [in] Node The node to check. + + @retval TRUE The Node is a ZeroOp or OneOp. + @retval FALSE Otherwise. +*/ +BOOLEAN +EFIAPI +IsSpecialIntegerNode ( + IN AML_OBJECT_NODE * Node + ); + +/** Check whether Node corresponds to a method definition. + + A method definition can be introduced: + - By a method object, having an AML_METHOD_OP OpCode; + - By an external definition of a method, having an AML_EXTERNAL_OP OpCode + and an ObjectType byte set to the MethodObj. + + Note: + An alias node, having an AML_ALIAS_OP, can be resolved to a method + definition. This function doesn't handle this case. + + @param [in] Node Node to check whether it is a method definition. + + @retval TRUE The Node is a method definition. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlIsMethodDefinitionNode ( + IN CONST AML_OBJECT_NODE * Node + ); + +/** Get the index at which the name of the node is stored. + + @param [in] ObjectNode Pointer to an object node. + Must have the AML_IN_NAMESPACE attribute. + @param [out] Index Index of the name in the fixed list of arguments. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +AmlNodeGetNameIndex ( + IN CONST AML_OBJECT_NODE * ObjectNode, + OUT EAML_PARSE_INDEX * Index + ); + +/** Get the name of the Node. + + Node must be part of the namespace. + + @param [in] ObjectNode Pointer to an object node, + which is part of the namespace. + + @return A pointer to the name. + NULL otherwise. + Return NULL for the root node. +**/ +CHAR8 * +EFIAPI +AmlNodeGetName ( + IN CONST AML_OBJECT_NODE * ObjectNode + ); + +#endif // AML_NODE_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c new file mode 100644 index 000000000..870346c40 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c @@ -0,0 +1,566 @@ +/** @file + AML Node Interface. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include +#include +#include +#include + +/** Returns the tree node type (Root/Object/Data). + + @param [in] Node Pointer to a Node. + + @return The node type. + EAmlNodeUnknown if invalid parameter. +**/ +EAML_NODE_TYPE +EFIAPI +AmlGetNodeType ( + IN AML_NODE_HEADER * Node + ) +{ + if (!IS_AML_NODE_VALID (Node)) { + ASSERT (0); + return EAmlNodeUnknown; + } + + return Node->NodeType; +} + +/** Get the RootNode information. + The Node must be a root node. + + @param [in] RootNode Pointer to a root node. + @param [out] SdtHeaderBuffer Buffer to copy the ACPI DSDT/SSDT header to. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetRootNodeInfo ( + IN AML_ROOT_NODE * RootNode, + OUT EFI_ACPI_DESCRIPTION_HEADER * SdtHeaderBuffer + ) +{ + if (!IS_AML_ROOT_NODE (RootNode) || + (SdtHeaderBuffer == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + CopyMem ( + SdtHeaderBuffer, + RootNode->SdtHeader, + sizeof (EFI_ACPI_DESCRIPTION_HEADER) + ); + + return EFI_SUCCESS; +} + +/** Get the ObjectNode information. + The Node must be an object node. + + @ingroup NodeInterfaceApi + + @param [in] ObjectNode Pointer to an object node. + @param [out] OpCode Pointer holding the OpCode. + Optional, can be NULL. + @param [out] SubOpCode Pointer holding the SubOpCode. + Optional, can be NULL. + @param [out] PkgLen Pointer holding the PkgLen. + The PkgLen is 0 for nodes + not having the Pkglen attribute. + Optional, can be NULL. + @param [out] IsNameSpaceNode Pointer holding TRUE if the node is defining + or changing the NameSpace scope. + E.g.: The "Name ()" and "Scope ()" ASL + statements add/modify the NameSpace scope. + Their corresponding node are NameSpace nodes. + Optional, can be NULL. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetObjectNodeInfo ( + IN AML_OBJECT_NODE * ObjectNode, + OUT UINT8 * OpCode, OPTIONAL + OUT UINT8 * SubOpCode, OPTIONAL + OUT UINT32 * PkgLen, OPTIONAL + OUT BOOLEAN * IsNameSpaceNode OPTIONAL + ) +{ + if (!IS_AML_OBJECT_NODE (ObjectNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (OpCode != NULL) { + *OpCode = ObjectNode->AmlByteEncoding->OpCode; + } + if (SubOpCode != NULL) { + *SubOpCode = ObjectNode->AmlByteEncoding->SubOpCode; + } + if (PkgLen != NULL) { + *PkgLen = ObjectNode->PkgLen; + } + if (IsNameSpaceNode != NULL) { + *IsNameSpaceNode = AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE); + } + + return EFI_SUCCESS; +} + +/** Returns the count of the fixed arguments for the input Node. + + @param [in] Node Pointer to an object node. + + @return Number of fixed arguments of the object node. + Return 0 if the node is not an object node. +**/ +UINT8 +AmlGetFixedArgumentCount ( + IN AML_OBJECT_NODE * Node + ) +{ + if (IS_AML_OBJECT_NODE (Node) && + (Node->AmlByteEncoding != NULL)) { + return (UINT8)Node->AmlByteEncoding->MaxIndex; + } + + return 0; +} + +/** Get the data type of the DataNode. + The Node must be a data node. + + @param [in] DataNode Pointer to a data node. + @param [out] DataType Pointer holding the data type of the data buffer. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetNodeDataType ( + IN AML_DATA_NODE * DataNode, + OUT EAML_NODE_DATA_TYPE * DataType + ) +{ + if (!IS_AML_DATA_NODE (DataNode) || + (DataType == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *DataType = DataNode->DataType; + + return EFI_SUCCESS; +} + +/** Get the descriptor Id of the resource data element + contained in the DataNode. + + The Node must be a data node. + The Node must have the resource data type, i.e. have the + EAmlNodeDataTypeResourceData data type. + + @param [in] DataNode Pointer to a data node containing a + resource data element. + @param [out] ResourceDataType Pointer holding the descriptor Id of + the resource data. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetResourceDataType ( + IN AML_DATA_NODE * DataNode, + OUT AML_RD_HEADER * ResourceDataType + ) +{ + if (!IS_AML_DATA_NODE (DataNode) || + (ResourceDataType == NULL) || + (DataNode->DataType != EAmlNodeDataTypeResourceData)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *ResourceDataType = AmlRdGetDescId (DataNode->Buffer); + + return EFI_SUCCESS; +} + +/** Get the data buffer and size of the DataNode. + The Node must be a data node. + + BufferSize is always updated to the size of buffer of the DataNode. + + If: + - the content of BufferSize is >= to the DataNode's buffer size; + - Buffer is not NULL; + then copy the content of the DataNode's buffer in Buffer. + + @param [in] DataNode Pointer to a data node. + @param [out] Buffer Buffer to write the data to. + Optional, if NULL, only update BufferSize. + @param [in, out] BufferSize Pointer holding: + - At entry, the size of the Buffer; + - At exit, the size of the DataNode's + buffer size. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetDataNodeBuffer ( + IN AML_DATA_NODE * DataNode, + OUT UINT8 * Buffer, OPTIONAL + IN OUT UINT32 * BufferSize + ) +{ + if (!IS_AML_DATA_NODE (DataNode) || + (BufferSize == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if ((*BufferSize >= DataNode->Size) && + (Buffer != NULL)) { + CopyMem (Buffer, DataNode->Buffer, DataNode->Size); + } + + *BufferSize = DataNode->Size; + + return EFI_SUCCESS; +} + +/** Update the ACPI DSDT/SSDT table header. + + The input SdtHeader information is copied to the tree RootNode. + The table Length field is automatically updated. + The checksum field is only updated when serializing the tree. + + @param [in] RootNode Pointer to a root node. + @param [in] SdtHeader Pointer to an ACPI DSDT/SSDT table header. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRootNode ( + IN AML_ROOT_NODE * RootNode, + IN CONST EFI_ACPI_DESCRIPTION_HEADER * SdtHeader + ) +{ + EFI_STATUS Status; + UINT32 Length; + + if (!IS_AML_ROOT_NODE (RootNode) || + (SdtHeader == NULL) || + ((SdtHeader->Signature != + EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) && + (SdtHeader->Signature != + EFI_ACPI_6_3_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + CopyMem ( + RootNode->SdtHeader, + SdtHeader, + sizeof (EFI_ACPI_DESCRIPTION_HEADER) + ); + + // Update the Length field. + Status = AmlComputeSize ((AML_NODE_HEADER*)RootNode, &Length); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + RootNode->SdtHeader->Length = Length + + (UINT32)sizeof (EFI_ACPI_DESCRIPTION_HEADER); + + return Status; +} + +/** Update an object node representing an integer with a new value. + + The object node must have one of the following OpCodes: + - AML_BYTE_PREFIX + - AML_WORD_PREFIX + - AML_DWORD_PREFIX + - AML_QWORD_PREFIX + - AML_ZERO_OP + - AML_ONE_OP + + The following OpCode is not supported: + - AML_ONES_OP + + @param [in] IntegerOpNode Pointer an object node containing an integer. + Must not be an object node with an AML_ONES_OP + OpCode. + @param [in] NewInteger New integer value to set. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlUpdateInteger ( + IN AML_OBJECT_NODE * IntegerOpNode, + IN UINT64 NewInteger + ) +{ + EFI_STATUS Status; + + INT8 ValueWidthDiff; + + if (!IS_AML_OBJECT_NODE (IntegerOpNode) || + (!IsIntegerNode (IntegerOpNode) && + !IsSpecialIntegerNode (IntegerOpNode)) || + AmlNodeCompareOpCode (IntegerOpNode, AML_ONES_OP, 0)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlNodeSetIntegerValue (IntegerOpNode, NewInteger, &ValueWidthDiff); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // If the new size is different from the old size, propagate the new size. + if (ValueWidthDiff != 0) { + // Propagate the information. + Status = AmlPropagateInformation ( + (AML_NODE_HEADER*)IntegerOpNode, + (ValueWidthDiff > 0) ? TRUE : FALSE, + ABS (ValueWidthDiff), + 0 + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + } + + return Status; +} + +/** Update the buffer of a data node. + + Note: The data type of the buffer's content must match the data type of the + DataNode. This is a hard restriction to prevent undesired behaviour. + + @param [in] DataNode Pointer to a data node. + @param [in] DataType Data type of the Buffer's content. + @param [in] Buffer Buffer containing the new data. The content of + the Buffer is copied. + @param [in] Size Size of the Buffer. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_UNSUPPORTED Operation not supporter. +**/ +EFI_STATUS +EFIAPI +AmlUpdateDataNode ( + IN AML_DATA_NODE * DataNode, + IN EAML_NODE_DATA_TYPE DataType, + IN UINT8 * Buffer, + IN UINT32 Size + ) +{ + EFI_STATUS Status; + + UINT32 ExpectedSize; + AML_OBJECT_NODE * ParentNode; + EAML_NODE_DATA_TYPE ExpectedArgType; + EAML_PARSE_INDEX Index; + + if (!IS_AML_DATA_NODE (DataNode) || + (DataType > EAmlNodeDataTypeMax) || + (Buffer == NULL) || + (Size == 0)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ParentNode = (AML_OBJECT_NODE*)AmlGetParent ((AML_NODE_HEADER*)DataNode); + if (!IS_AML_OBJECT_NODE (ParentNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // The NewNode and OldNode must have the same type. + // We do not allow to change the argument type of a data node. + // If required, the initial ASL template should be modified + // accordingly. + // It is however possible to interchange a raw buffer and a + // resource data element, since raw data can be misinterpreted + // as a resource data element. + ExpectedArgType = DataNode->DataType; + if ((ExpectedArgType != DataType) && + (((ExpectedArgType != EAmlNodeDataTypeRaw) && + (ExpectedArgType != EAmlNodeDataTypeResourceData)) || + ((DataType != EAmlNodeDataTypeRaw) && + (DataType != EAmlNodeDataTypeResourceData)))) { + ASSERT (0); + return EFI_UNSUPPORTED; + } + + // Perform some compatibility checks. + switch (DataType) { + case EAmlNodeDataTypeNameString: + { + // Check the name contained in the Buffer is an AML name + // with the right size. + Status = AmlGetNameStringSize ((CONST CHAR8*)Buffer, &ExpectedSize); + if (EFI_ERROR (Status) || + (Size != ExpectedSize)) { + ASSERT (0); + return Status; + } + break; + } + case EAmlNodeDataTypeString: + { + ExpectedSize = 0; + while (ExpectedSize < Size) { + // Cf ACPI 6.3 specification 20.2.3 Data Objects Encoding. + // AsciiCharList := Nothing | + // AsciiChar := 0x01 - 0x7F + // NullChar := 0x00 + if (Buffer[ExpectedSize] > 0x7F) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + ExpectedSize++; + } + + if (ExpectedSize != Size) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + break; + } + case EAmlNodeDataTypeUInt: + { + if (AmlIsNodeFixedArgument ((CONST AML_NODE_HEADER*)DataNode, &Index)) { + if ((ParentNode->AmlByteEncoding == NULL) || + (ParentNode->AmlByteEncoding->Format == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // It is not possible to change the size of a fixed length UintX. + // E.g. for PackageOp the first fixed argument is of type EAmlUInt8 + // and represents the count of elements. This type cannot be changed. + if ((ParentNode->AmlByteEncoding->Format[Index] != EAmlObject) && + (DataNode->Size != Size)) { + ASSERT (0); + return EFI_UNSUPPORTED; + } + } + break; + } + case EAmlNodeDataTypeRaw: + { + // Check if the parent node has the byte list flag set. + if (!AmlNodeHasAttribute (ParentNode, AML_HAS_BYTE_LIST)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + break; + } + case EAmlNodeDataTypeResourceData: + { + // The resource data can be either small or large resource data. + // Small resource data must be at least 1 byte. + // Large resource data must be at least as long as the header + // of a large resource data. + if (AML_RD_IS_LARGE (Buffer) && + (Size < sizeof (ACPI_LARGE_RESOURCE_HEADER))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Check if the parent node has the byte list flag set. + if (!AmlNodeHasAttribute (ParentNode, AML_HAS_BYTE_LIST)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Check the size of the buffer is equal to the resource data size + // encoded in the input buffer. + ExpectedSize = AmlRdGetSize (Buffer); + if (ExpectedSize != Size) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + break; + } + case EAmlNodeDataTypeFieldPkgLen: + { + // Check the parent is a FieldNamed field element. + if (!AmlNodeCompareOpCode (ParentNode, AML_FIELD_NAMED_OP, 0)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + break; + } + // None and reserved types. + default: + { + ASSERT (0); + return EFI_INVALID_PARAMETER; + break; + } + } // switch + + // If the new size is different from the old size, propagate the new size. + if (DataNode->Size != Size) { + // Propagate the information. + Status = AmlPropagateInformation ( + DataNode->NodeHeader.Parent, + (Size > DataNode->Size) ? TRUE : FALSE, + (Size > DataNode->Size) ? + (Size - DataNode->Size) : + (DataNode->Size - Size), + 0 + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Free the old DataNode buffer and allocate a new buffer to store the + // new data. + FreePool (DataNode->Buffer); + DataNode->Buffer = AllocateZeroPool (Size); + if (DataNode->Buffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + DataNode->Size = Size; + } + + CopyMem (DataNode->Buffer, Buffer, Size); + + return EFI_SUCCESS; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c new file mode 100644 index 000000000..65dad95da --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c @@ -0,0 +1,1047 @@ +/** @file + AML Tree. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include +#include + +/** Get the parent node of the input Node. + + @param [in] Node Pointer to a node. + + @return The parent node of the input Node. + NULL otherwise. +**/ +AML_NODE_HEADER * +EFIAPI +AmlGetParent ( + IN AML_NODE_HEADER * Node + ) +{ + if (IS_AML_DATA_NODE (Node) || + IS_AML_OBJECT_NODE (Node)) { + return Node->Parent; + } + + return NULL; +} + +/** Get the root node from any node of the tree. + This is done by climbing up the tree until the root node is reached. + + @param [in] Node Pointer to a node. + + @return The root node of the tree. + NULL if error. +*/ +AML_ROOT_NODE * +EFIAPI +AmlGetRootNode ( + IN CONST AML_NODE_HEADER * Node + ) +{ + if (!IS_AML_NODE_VALID (Node)) { + ASSERT (0); + return NULL; + } + + while (!IS_AML_ROOT_NODE (Node)) { + Node = Node->Parent; + if (!IS_AML_NODE_VALID (Node)) { + ASSERT (0); + return NULL; + } + } + return (AML_ROOT_NODE*)Node; +} + +/** Get the node at the input Index in the fixed argument list of the input + ObjectNode. + + @param [in] ObjectNode Pointer to an object node. + @param [in] Index The Index of the fixed argument to get. + + @return The node at the input Index in the fixed argument list + of the input ObjectNode. + NULL otherwise, e.g. if the node is not an object node, or no + node is available at this Index. +**/ +AML_NODE_HEADER * +EFIAPI +AmlGetFixedArgument ( + IN AML_OBJECT_NODE * ObjectNode, + IN EAML_PARSE_INDEX Index + ) +{ + if (IS_AML_OBJECT_NODE (ObjectNode)) { + if (Index < (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (ObjectNode)) { + return ObjectNode->FixedArgs[Index]; + } + } + + return NULL; +} + +/** Check whether the input Node is in the fixed argument list of its parent + node. + + If so, IndexPtr contains this Index. + + @param [in] Node Pointer to a Node. + @param [out] IndexPtr Pointer holding the Index of the Node in + its parent's fixed argument list. + + @retval TRUE The node is a fixed argument and the index + in IndexPtr is valid. + @retval FALSE The node is not a fixed argument. +**/ +BOOLEAN +EFIAPI +AmlIsNodeFixedArgument ( + IN CONST AML_NODE_HEADER * Node, + OUT EAML_PARSE_INDEX * IndexPtr + ) +{ + AML_NODE_HEADER * ParentNode; + + EAML_PARSE_INDEX Index; + EAML_PARSE_INDEX MaxIndex; + + if ((IndexPtr == NULL) || + (!IS_AML_DATA_NODE (Node) && + !IS_AML_OBJECT_NODE (Node))) { + ASSERT (0); + return FALSE; + } + + ParentNode = AmlGetParent ((AML_NODE_HEADER*)Node); + if (IS_AML_ROOT_NODE (ParentNode)) { + return FALSE; + } else if (IS_AML_DATA_NODE (ParentNode)) { + // Tree is inconsistent. + ASSERT (0); + return FALSE; + } + + // Check whether the Node is in the fixed argument list. + MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount ( + (AML_OBJECT_NODE*)ParentNode + ); + for (Index = EAmlParseIndexTerm0; Index < MaxIndex; Index++) { + if (AmlGetFixedArgument ((AML_OBJECT_NODE*)ParentNode, Index) == Node) { + *IndexPtr = Index; + return TRUE; + } + } + + return FALSE; +} + +/** Set the fixed argument of the ObjectNode at the Index to the NewNode. + + It is the caller's responsibility to save the old node, if desired, + otherwise the reference to the old node will be lost. + If NewNode is not NULL, set its parent to ObjectNode. + + @param [in] ObjectNode Pointer to an object node. + @param [in] Index Index in the fixed argument list of + the ObjectNode to set. + @param [in] NewNode Pointer to the NewNode. + Can be NULL, a data node or an object node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlSetFixedArgument ( + IN AML_OBJECT_NODE * ObjectNode, + IN EAML_PARSE_INDEX Index, + IN AML_NODE_HEADER * NewNode + ) +{ + if (IS_AML_OBJECT_NODE (ObjectNode) && + (Index <= (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (ObjectNode)) && + ((NewNode == NULL) || + IS_AML_OBJECT_NODE (NewNode) || + IS_AML_DATA_NODE (NewNode))) { + ObjectNode->FixedArgs[Index] = NewNode; + + // If NewNode is a data node or an object node, set its parent. + if (NewNode != NULL) { + NewNode->Parent = (AML_NODE_HEADER*)ObjectNode; + } + + return EFI_SUCCESS; + } + + ASSERT (0); + return EFI_INVALID_PARAMETER; +} + +/** If the given AML_NODE_HEADER has a variable list of arguments, + return a pointer to this list. + Return NULL otherwise. + + @param [in] Node Pointer to the AML_NODE_HEADER to check. + + @return The list of variable arguments if there is one. + NULL otherwise. +**/ +LIST_ENTRY * +EFIAPI +AmlNodeGetVariableArgList ( + IN CONST AML_NODE_HEADER * Node + ) +{ + if (IS_AML_ROOT_NODE (Node)) { + return &(((AML_ROOT_NODE*)Node)->VariableArgs); + } else if (IS_AML_OBJECT_NODE (Node)) { + return &(((AML_OBJECT_NODE*)Node)->VariableArgs); + } + return NULL; +} + +/** Remove the Node from its parent's variable list of arguments. + + The function will fail if the Node is in its parent's fixed + argument list. + The Node is not deleted. The deletion is done separately + from the removal. + + @param [in] Node Pointer to a Node. + Must be a data node or an object node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlRemoveNodeFromVarArgList ( + IN AML_NODE_HEADER * Node + ) +{ + EFI_STATUS Status; + AML_NODE_HEADER * ParentNode; + UINT32 Size; + + if ((!IS_AML_DATA_NODE (Node) && + !IS_AML_OBJECT_NODE (Node))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ParentNode = AmlGetParent (Node); + if (!IS_AML_ROOT_NODE (ParentNode) && + !IS_AML_OBJECT_NODE (ParentNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Check the node is in its parent variable list of arguments. + if (!IsNodeInList ( + AmlNodeGetVariableArgList (ParentNode), + &Node->Link)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Unlink Node from the tree. + RemoveEntryList (&Node->Link); + InitializeListHead (&Node->Link); + Node->Parent = NULL; + + // Get the size of the node removed. + Status = AmlComputeSize (Node, &Size); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Propagate the information. + Status = AmlPropagateInformation (ParentNode, FALSE, Size, 1); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Detach the Node from the tree. + + The function will fail if the Node is in its parent's fixed + argument list. + The Node is not deleted. The deletion is done separately + from the removal. + + @param [in] Node Pointer to a Node. + Must be a data node or an object node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDetachNode ( + IN AML_NODE_HEADER * Node + ) +{ + return AmlRemoveNodeFromVarArgList (Node); +} + +/** Add the NewNode to the head of the variable list of arguments + of the ParentNode. + + @param [in] ParentNode Pointer to the parent node. + Must be a root or an object node. + @param [in] NewNode Pointer to the node to add. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlVarListAddHead ( + IN AML_NODE_HEADER * ParentNode, + IN AML_NODE_HEADER * NewNode + ) +{ + EFI_STATUS Status; + UINT32 NewSize; + LIST_ENTRY * ChildrenList; + + // Check arguments and that NewNode is not already attached to a tree. + // ParentNode != Data Node AND NewNode != Root Node AND NewNode != attached. + if ((!IS_AML_ROOT_NODE (ParentNode) && + !IS_AML_OBJECT_NODE (ParentNode)) || + (!IS_AML_DATA_NODE (NewNode) && + !IS_AML_OBJECT_NODE (NewNode)) || + !AML_NODE_IS_DETACHED (NewNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Insert it at the head of the list. + ChildrenList = AmlNodeGetVariableArgList (ParentNode); + if (ChildrenList == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + InsertHeadList (ChildrenList, &NewNode->Link); + NewNode->Parent = ParentNode; + + // Get the size of the NewNode. + Status = AmlComputeSize (NewNode, &NewSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Propagate the new information. + Status = AmlPropagateInformation (ParentNode, TRUE, NewSize, 1); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Add the NewNode to the tail of the variable list of arguments + of the ParentNode. + + NOTE: This is an internal function which does not propagate the size + when a new node is added. + + @param [in] ParentNode Pointer to the parent node. + Must be a root or an object node. + @param [in] NewNode Pointer to the node to add. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlVarListAddTailInternal ( + IN AML_NODE_HEADER * ParentNode, + IN AML_NODE_HEADER * NewNode + ) +{ + LIST_ENTRY * ChildrenList; + + // Check arguments and that NewNode is not already attached to a tree. + // ParentNode != Data Node AND NewNode != Root Node AND NewNode != attached. + if ((!IS_AML_ROOT_NODE (ParentNode) && + !IS_AML_OBJECT_NODE (ParentNode)) || + (!IS_AML_DATA_NODE (NewNode) && + !IS_AML_OBJECT_NODE (NewNode)) || + !AML_NODE_IS_DETACHED (NewNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Insert it at the tail of the list. + ChildrenList = AmlNodeGetVariableArgList (ParentNode); + if (ChildrenList == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + InsertTailList (ChildrenList, &NewNode->Link); + NewNode->Parent = ParentNode; + + return EFI_SUCCESS; +} + +/** Add the NewNode to the tail of the variable list of arguments + of the ParentNode. + + @param [in] ParentNode Pointer to the parent node. + Must be a root or an object node. + @param [in] NewNode Pointer to the node to add. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlVarListAddTail ( + IN AML_NODE_HEADER * ParentNode, + IN AML_NODE_HEADER * NewNode + ) +{ + EFI_STATUS Status; + UINT32 NewSize; + + // Add the NewNode and check arguments. + Status = AmlVarListAddTailInternal (ParentNode, NewNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Get the size of the NewNode. + Status = AmlComputeSize (NewNode, &NewSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Propagate the new information. + Status = AmlPropagateInformation (ParentNode, TRUE, NewSize, 1); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Add the NewNode before the Node in the list of variable + arguments of the Node's parent. + + @param [in] Node Pointer to a node. + Must be a root or an object node. + @param [in] NewNode Pointer to the node to add. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlVarListAddBefore ( + IN AML_NODE_HEADER * Node, + IN AML_NODE_HEADER * NewNode + ) +{ + EFI_STATUS Status; + AML_NODE_HEADER * ParentNode; + UINT32 NewSize; + + // Check arguments and that NewNode is not already attached to a tree. + if ((!IS_AML_DATA_NODE (NewNode) && + !IS_AML_OBJECT_NODE (NewNode)) || + !AML_NODE_IS_DETACHED (NewNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ParentNode = AmlGetParent (Node); + if (!IS_AML_ROOT_NODE (ParentNode) && + !IS_AML_OBJECT_NODE (ParentNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Insert it before the input Node. + InsertTailList (&Node->Link, &NewNode->Link); + NewNode->Parent = ParentNode; + + // Get the size of the NewNode. + Status = AmlComputeSize (NewNode, &NewSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Propagate the new information. + Status = AmlPropagateInformation (ParentNode, TRUE, NewSize, 1); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Add the NewNode after the Node in the variable list of arguments + of the Node's parent. + + @param [in] Node Pointer to a node. + Must be a root or an object node. + @param [in] NewNode Pointer to the node to add. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlVarListAddAfter ( + IN AML_NODE_HEADER * Node, + IN AML_NODE_HEADER * NewNode + ) +{ + EFI_STATUS Status; + AML_NODE_HEADER * ParentNode; + UINT32 NewSize; + + // Check arguments and that NewNode is not already attached to a tree. + if ((!IS_AML_DATA_NODE (NewNode) && + !IS_AML_OBJECT_NODE (NewNode)) || + !AML_NODE_IS_DETACHED (NewNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ParentNode = AmlGetParent (Node); + if (!IS_AML_ROOT_NODE (ParentNode) && + !IS_AML_OBJECT_NODE (ParentNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Insert the new node after the input Node. + InsertHeadList (&Node->Link, &NewNode->Link); + NewNode->Parent = ParentNode; + + // Get the size of the NewNode. + Status = AmlComputeSize (NewNode, &NewSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Propagate the new information. + Status = AmlPropagateInformation (ParentNode, TRUE, NewSize, 1); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Append a Resource Data node to the BufferOpNode. + + The Resource Data node is added at the end of the variable + list of arguments of the BufferOpNode, but before the End Tag. + If no End Tag is found, the function returns an error. + + @param [in] BufferOpNode Buffer node containing resource data elements. + @param [in] NewRdNode The new Resource Data node to add. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlAppendRdNode ( + IN AML_OBJECT_NODE * BufferOpNode, + IN AML_DATA_NODE * NewRdNode + ) +{ + EFI_STATUS Status; + AML_DATA_NODE * CurrRdNode; + AML_RD_HEADER RdDataType; + + if (!AmlNodeCompareOpCode (BufferOpNode, AML_BUFFER_OP, 0) || + !IS_AML_DATA_NODE (NewRdNode) || + (NewRdNode->DataType != EAmlNodeDataTypeResourceData)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the first Resource data node in the variable list of + // argument of the BufferOp node. + CurrRdNode = (AML_DATA_NODE*)AmlGetNextVariableArgument ( + (AML_NODE_HEADER*)BufferOpNode, + NULL + ); + if ((CurrRdNode == NULL) || + !IS_AML_DATA_NODE (CurrRdNode) || + (CurrRdNode->DataType != EAmlNodeDataTypeResourceData)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Iterate through the Resource Data nodes to find the End Tag. + while (TRUE) { + Status = AmlGetResourceDataType (CurrRdNode, &RdDataType); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // If the Resource Data is an End Tag, + // add the new node before and return. + if (AmlRdCompareDescId ( + &RdDataType, + AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) { + Status = AmlVarListAddBefore ( + (AML_NODE_HEADER*)CurrRdNode, + (AML_NODE_HEADER*)NewRdNode) + ; + if (EFI_ERROR (Status)) { + ASSERT (0); + } + return Status; + } + + // Get the next Resource Data node. + // If this was the last node and no End Tag was found, return error. + // It is possible to have only one Resource Data in a BufferOp, + // but it should not be possible to add a new Resource Data in the list + // in this case. + CurrRdNode = (AML_DATA_NODE*)AmlGetSiblingVariableArgument ( + (AML_NODE_HEADER*)CurrRdNode + ); + if (!IS_AML_DATA_NODE (CurrRdNode) || + (CurrRdNode->DataType != EAmlNodeDataTypeResourceData)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } // while +} + +/** Replace the fixed argument at the Index of the ParentNode with the NewNode. + + Note: This function unlinks the OldNode from the tree. It is the callers + responsibility to delete the OldNode if needed. + + @param [in] ParentNode Pointer to the parent node. + Must be an object node. + @param [in] Index Index of the fixed argument to replace. + @param [in] NewNode The new node to insert. + Must be an object node or a data node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlReplaceFixedArgument ( + IN AML_OBJECT_NODE * ParentNode, + IN EAML_PARSE_INDEX Index, + IN AML_NODE_HEADER * NewNode + ) +{ + EFI_STATUS Status; + + AML_NODE_HEADER * OldNode; + UINT32 NewSize; + UINT32 OldSize; + AML_PARSE_FORMAT FixedArgType; + + // Check arguments and that NewNode is not already attached to a tree. + if (!IS_AML_OBJECT_NODE (ParentNode) || + (!IS_AML_DATA_NODE (NewNode) && + !IS_AML_OBJECT_NODE (NewNode)) || + !AML_NODE_IS_DETACHED (NewNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Perform some compatibility checks between NewNode and OldNode. + FixedArgType = ParentNode->AmlByteEncoding->Format[Index]; + switch (FixedArgType) { + case EAmlFieldPkgLen: + { + // A FieldPkgLen can only have a parent node with the + // AML_IS_FIELD_ELEMENT flag. + if (!AmlNodeHasAttribute ( + (AML_OBJECT_NODE*)ParentNode, + AML_HAS_FIELD_LIST)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + // Fall through. + } + + case EAmlUInt8: + case EAmlUInt16: + case EAmlUInt32: + case EAmlUInt64: + case EAmlName: + case EAmlString: + { + // A uint, a name, a string and a FieldPkgLen can only be replaced by a + // data node of the same type. + // Note: This condition might be too strict, but safer. + if (!IS_AML_DATA_NODE (NewNode) || + (((AML_DATA_NODE*)NewNode)->DataType != + AmlTypeToNodeDataType (FixedArgType))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + break; + } + + case EAmlObject: + { + // If it's an object node, the grammar is too complex to do any check. + break; + } + + case EAmlNone: + default: + { + ASSERT (0); + return EFI_INVALID_PARAMETER; + break; + } + } // switch + + // Replace the OldNode with the NewNode. + OldNode = AmlGetFixedArgument (ParentNode, Index); + if (!IS_AML_NODE_VALID (OldNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Unlink the old node. + // Note: This function unlinks the OldNode from the tree. It is the callers + // responsibility to delete the OldNode if needed. + OldNode->Parent = NULL; + + Status = AmlSetFixedArgument (ParentNode, Index, NewNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Get the size of the OldNode. + Status = AmlComputeSize (OldNode, &OldSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Get the size of the NewNode. + Status = AmlComputeSize (NewNode, &NewSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Propagate the new information. + Status = AmlPropagateInformation ( + (AML_NODE_HEADER*)ParentNode, + (NewSize > OldSize) ? TRUE : FALSE, + (NewSize > OldSize) ? (NewSize - OldSize) : (OldSize - NewSize), + 0 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Replace the OldNode, which is in a variable list of arguments, + with the NewNode. + + Note: This function unlinks the OldNode from the tree. It is the callers + responsibility to delete the OldNode if needed. + + @param [in] OldNode Pointer to the node to replace. + Must be a data node or an object node. + @param [in] NewNode The new node to insert. + Must be a data node or an object node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlReplaceVariableArgument ( + IN AML_NODE_HEADER * OldNode, + IN AML_NODE_HEADER * NewNode + ) +{ + EFI_STATUS Status; + UINT32 NewSize; + UINT32 OldSize; + EAML_PARSE_INDEX Index; + + AML_DATA_NODE * NewDataNode; + AML_NODE_HEADER * ParentNode; + LIST_ENTRY * NextLink; + + // Check arguments, that NewNode is not already attached to a tree, + // and that OldNode is attached and not in a fixed list of arguments. + if ((!IS_AML_DATA_NODE (OldNode) && + !IS_AML_OBJECT_NODE (OldNode)) || + (!IS_AML_DATA_NODE (NewNode) && + !IS_AML_OBJECT_NODE (NewNode)) || + !AML_NODE_IS_DETACHED (NewNode) || + AML_NODE_IS_DETACHED (OldNode) || + AmlIsNodeFixedArgument (OldNode, &Index)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ParentNode = AmlGetParent (OldNode); + if (!IS_AML_ROOT_NODE (ParentNode) && + !IS_AML_OBJECT_NODE (ParentNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + NewDataNode = (AML_DATA_NODE*)NewNode; + + // Check attributes if the parent node is an object node. + if (IS_AML_OBJECT_NODE (ParentNode)) { + // A child node of a node with the HAS_CHILD flag must be either a + // data node or an object node. This has already been checked. So, + // check for other cases. + + if (AmlNodeHasAttribute ((AML_OBJECT_NODE*)ParentNode, AML_HAS_BYTE_LIST)) { + if (!IS_AML_DATA_NODE (NewNode) || + ((NewDataNode->DataType != EAmlNodeDataTypeRaw) && + (NewDataNode->DataType != EAmlNodeDataTypeResourceData))) { + // A child node of a node with the BYTE_LIST flag must be a data node, + // containing raw data or a resource data. + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } else if (AmlNodeHasAttribute ( + (AML_OBJECT_NODE*)ParentNode, + AML_HAS_FIELD_LIST)) { + if (!AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)NewNode, + AML_IS_FIELD_ELEMENT)) { + // A child node of a node with the FIELD_LIST flag must be an object + // node with AML_IS_FIELD_ELEMENT flag. + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } + } else { + // Parent node is a root node. + // A root node cannot have a data node as its child. + if (!IS_AML_DATA_NODE (NewNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } + + // Unlink OldNode from the tree. + NextLink = RemoveEntryList (&OldNode->Link); + InitializeListHead (&OldNode->Link); + OldNode->Parent = NULL; + + // Add the NewNode. + InsertHeadList (NextLink, &NewNode->Link); + NewNode->Parent = ParentNode; + + // Get the size of the OldNode. + Status = AmlComputeSize (OldNode, &OldSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Get the size of the NewNode. + Status = AmlComputeSize (NewNode, &NewSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Propagate the new information. + Status = AmlPropagateInformation ( + ParentNode, + (NewSize > OldSize) ? TRUE : FALSE, + (NewSize > OldSize) ? (NewSize - OldSize) : (OldSize - NewSize), + 0 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Replace the OldNode by the NewNode. + + Note: This function unlinks the OldNode from the tree. It is the callers + responsibility to delete the OldNode if needed. + + @param [in] OldNode Pointer to the node to replace. + Must be a data node or an object node. + @param [in] NewNode The new node to insert. + Must be a data node or an object node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlReplaceArgument ( + IN AML_NODE_HEADER * OldNode, + IN AML_NODE_HEADER * NewNode + ) +{ + EFI_STATUS Status; + AML_NODE_HEADER * ParentNode; + EAML_PARSE_INDEX Index; + + // Check arguments and that NewNode is not already attached to a tree. + if ((!IS_AML_DATA_NODE (OldNode) && + !IS_AML_OBJECT_NODE (OldNode)) || + (!IS_AML_DATA_NODE (NewNode) && + !IS_AML_OBJECT_NODE (NewNode)) || + !AML_NODE_IS_DETACHED (NewNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // ParentNode can be a root node or an object node. + ParentNode = AmlGetParent (OldNode); + if (!IS_AML_ROOT_NODE (ParentNode) && + !IS_AML_OBJECT_NODE (ParentNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (AmlIsNodeFixedArgument (OldNode, &Index)) { + // OldNode is in its parent's fixed argument list at the Index. + Status = AmlReplaceFixedArgument ( + (AML_OBJECT_NODE*)ParentNode, + Index, + NewNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } else { + // OldNode is not in its parent's fixed argument list. + // It must be in its variable list of arguments. + Status = AmlReplaceVariableArgument (OldNode, NewNode); + ASSERT_EFI_ERROR (Status); + } + + return Status; +} + +/** Delete a Node and its children. + + The Node must be removed from the tree first, + or must be the root node. + + @param [in] Node Pointer to the node to delete. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeleteTree ( + IN AML_NODE_HEADER * Node + ) +{ + EFI_STATUS Status; + + EAML_PARSE_INDEX Index; + EAML_PARSE_INDEX MaxIndex; + + AML_NODE_HEADER * Arg; + LIST_ENTRY * StartLink; + LIST_ENTRY * CurrentLink; + LIST_ENTRY * NextLink; + + // Check that the node being deleted is unlinked. + // When removing the node, its parent pointer and + // its lists data structure are reset with + // InitializeListHead. Thus it must be detached + // from the tree to avoid memory leaks. + if (!IS_AML_NODE_VALID (Node) || + !AML_NODE_IS_DETACHED (Node)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // 1. Recursively detach and delete the fixed arguments. + // Iterate through the fixed list of arguments. + if (IS_AML_OBJECT_NODE (Node)) { + MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount ( + (AML_OBJECT_NODE*)Node + ); + for (Index = EAmlParseIndexTerm0; Index < MaxIndex; Index++) { + Arg = AmlGetFixedArgument ((AML_OBJECT_NODE*)Node, Index); + if (Arg == NULL) { + // A fixed argument is missing. The tree is inconsistent. + // Note: During CodeGeneration, the fixed arguments should be set + // with an incrementing index, and then the variable arguments + // should be added. This allows to free as many nodes as + // possible if a crash occurs. + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Remove the node from the fixed argument list. + Arg->Parent = NULL; + Status = AmlSetFixedArgument ((AML_OBJECT_NODE*)Node, Index, NULL); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlDeleteTree (Arg); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + } + + // 2. Recursively detach and delete the variable arguments. + // Iterate through the variable list of arguments. + StartLink = AmlNodeGetVariableArgList (Node); + if (StartLink != NULL) { + NextLink = StartLink->ForwardLink; + while (NextLink != StartLink) { + CurrentLink = NextLink; + + // Unlink the node from the tree. + NextLink = RemoveEntryList (CurrentLink); + InitializeListHead (CurrentLink); + ((AML_NODE_HEADER*)CurrentLink)->Parent = NULL; + + Status = AmlDeleteTree ((AML_NODE_HEADER*)CurrentLink); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } // while + } + + // 3. Delete the node. + Status = AmlDeleteNode (Node); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h new file mode 100644 index 000000000..0b3803c47 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h @@ -0,0 +1,127 @@ +/** @file + AML Tree. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_TREE_H_ +#define AML_TREE_H_ + +#include + +/** Get the root node from any node of the tree. + This is done by climbing up the tree until the root node is reached. + + @param [in] Node Pointer to a node. + + @return The root node of the tree. + NULL if error. +*/ +AML_ROOT_NODE * +EFIAPI +AmlGetRootNode ( + IN CONST AML_NODE_HEADER * Node + ); + +/** Check whether the input Node is in the fixed argument list of its parent + node. + + If so, IndexPtr contains this Index. + + @param [in] Node Pointer to a Node. + @param [out] IndexPtr Pointer holding the Index of the Node in + its parent's fixed argument list. + + @retval TRUE The node is a fixed argument and the index + in IndexPtr is valid. + @retval FALSE The node is not a fixed argument. +**/ +BOOLEAN +EFIAPI +AmlIsNodeFixedArgument ( + IN CONST AML_NODE_HEADER * Node, + OUT EAML_PARSE_INDEX * IndexPtr + ); + +/** Set the fixed argument of the ObjectNode at the Index to the NewNode. + + It is the caller's responsibility to save the old node, if desired, + otherwise the reference to the old node will be lost. + If NewNode is not NULL, set its parent to ObjectNode. + + @param [in] ObjectNode Pointer to an object node. + @param [in] Index Index in the fixed argument list of + the ObjectNode to set. + @param [in] NewNode Pointer to the NewNode. + Can be NULL, a data node or an object node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlSetFixedArgument ( + IN AML_OBJECT_NODE * ObjectNode, + IN EAML_PARSE_INDEX Index, + IN AML_NODE_HEADER * NewNode + ); + +/** If the given AML_NODE_HEADER has a variable list of arguments, + return a pointer to this list. + Return NULL otherwise. + + @param [in] Node Pointer to the AML_NODE_HEADER to check. + + @return The list of variable arguments if there is one. + NULL otherwise. +**/ +LIST_ENTRY * +EFIAPI +AmlNodeGetVariableArgList ( + IN CONST AML_NODE_HEADER * Node + ); + +/** Add the NewNode to the tail of the variable list of arguments + of the ParentNode. + + NOTE: This is an internal function which does not propagate the size + when a new node is added. + + @param [in] ParentNode Pointer to the parent node. + Must be a root or an object node. + @param [in] NewNode Pointer to the node to add. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlVarListAddTailInternal ( + IN AML_NODE_HEADER * ParentNode, + IN AML_NODE_HEADER * NewNode + ); + +/** Replace the OldNode by the NewNode. + + Note: This function unlinks the OldNode from the tree. It is the callers + responsibility to delete the OldNode if needed. + + @param [in] OldNode Pointer to the node to replace. + Must be a data node or an object node. + @param [in] NewNode The new node to insert. + Must be a data node or an object node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlReplaceArgument ( + IN AML_NODE_HEADER * OldNode, + IN AML_NODE_HEADER * NewNode + ); + +#endif // AML_TREE_H_ + diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c new file mode 100644 index 000000000..3eeb7253b --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c @@ -0,0 +1,98 @@ +/** @file + AML Tree Enumerator. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include + +/** Enumerate all nodes of the subtree under the input Node in the AML + bytestream order (i.e. in a depth first order), and call the CallBack + function with the input Context. + The prototype of the Callback function is EDKII_AML_TREE_ENUM_CALLBACK. + + @param [in] Node Enumerate nodes of the subtree under this Node. + Must be a valid node. + @param [in] CallBack Callback function to call on each node. + @param [in, out] Context Void pointer used to pass some information + to the Callback function. + Optional, can be NULL. + @param [out] Status Optional parameter that can be used to get + the status of the Callback function. + If used, need to be init to EFI_SUCCESS. + + @retval TRUE if the enumeration can continue or has finished without + interruption. + @retval FALSE if the enumeration needs to stopped or has stopped. +**/ +BOOLEAN +EFIAPI +AmlEnumTree ( + IN AML_NODE_HEADER * Node, + IN EDKII_AML_TREE_ENUM_CALLBACK CallBack, + IN OUT VOID * Context, OPTIONAL + OUT EFI_STATUS * Status OPTIONAL + ) +{ + BOOLEAN ContinueEnum; + + EAML_PARSE_INDEX Index; + EAML_PARSE_INDEX MaxIndex; + + LIST_ENTRY * StartLink; + LIST_ENTRY * CurrentLink; + + if (!IS_AML_NODE_VALID (Node) || (CallBack == NULL)) { + ASSERT (0); + if (Status != NULL) { + *Status = EFI_INVALID_PARAMETER; + } + return FALSE; + } + + ContinueEnum = (*CallBack)(Node, Context, Status); + if (ContinueEnum == FALSE) { + return ContinueEnum; + } + + // Iterate through the fixed list of arguments. + MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount ( + (AML_OBJECT_NODE*)Node + ); + for (Index = EAmlParseIndexTerm0; Index < MaxIndex; Index++) { + ContinueEnum = AmlEnumTree ( + AmlGetFixedArgument ((AML_OBJECT_NODE*)Node, Index), + CallBack, + Context, + Status + ); + if (ContinueEnum == FALSE) { + return ContinueEnum; + } + } + + // Iterate through the variable list of arguments. + StartLink = AmlNodeGetVariableArgList (Node); + if (StartLink != NULL) { + CurrentLink = StartLink->ForwardLink; + while (CurrentLink != StartLink) { + ContinueEnum = AmlEnumTree ( + (AML_NODE_HEADER*)CurrentLink, + CallBack, + Context, + Status + ); + if (ContinueEnum == FALSE) { + return ContinueEnum; + } + CurrentLink = CurrentLink->ForwardLink; + } // while + } + + return ContinueEnum; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c new file mode 100644 index 000000000..e99c23499 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c @@ -0,0 +1,353 @@ +/** @file + AML Tree Iterator. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include + +#include +#include + +/** Iterator to traverse the tree. + + This is an internal structure. +*/ +typedef struct AmlTreeInternalIterator { + /// External iterator structure, containing the external APIs. + /// Must be the first field. + AML_TREE_ITERATOR Iterator; + + // Note: The following members of this structure are opaque to the users + // of the Tree iterator APIs. + + /// Pointer to the node on which the iterator has been initialized. + CONST AML_NODE_HEADER * InitialNode; + + /// Pointer to the current node. + CONST AML_NODE_HEADER * CurrentNode; + + /// Iteration mode. + /// Allow to choose how to traverse the tree/choose which node is next. + EAML_ITERATOR_MODE Mode; +} AML_TREE_ITERATOR_INTERNAL; + +/** Get the current node of an iterator. + + @param [in] Iterator Pointer to an iterator. + @param [out] OutNode Pointer holding the current node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlIteratorGetNode ( + IN AML_TREE_ITERATOR * Iterator, + OUT AML_NODE_HEADER ** OutNode + ) +{ + AML_TREE_ITERATOR_INTERNAL * InternalIterator; + + InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator; + + // CurrentNode can be NULL, but InitialNode cannot. + if ((OutNode == NULL) || + (InternalIterator == NULL) || + (InternalIterator->Mode <= EAmlIteratorUnknown) || + (InternalIterator->Mode >= EAmlIteratorModeMax) || + !IS_AML_NODE_VALID (InternalIterator->InitialNode) || + ((InternalIterator->CurrentNode != NULL) && + !IS_AML_NODE_VALID (InternalIterator->CurrentNode))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutNode = (AML_NODE_HEADER*)InternalIterator->CurrentNode; + + return EFI_SUCCESS; +} + +/** Move the current node of the iterator to the next node, + according to the iteration mode selected. + + If NextNode is not NULL, return the next node. + + @param [in] Iterator Pointer to an iterator. + @param [out] NextNode If not NULL, updated to the next node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlIteratorGetNextLinear ( + IN AML_TREE_ITERATOR * Iterator, + OUT AML_NODE_HEADER ** NextNode + ) +{ + AML_TREE_ITERATOR_INTERNAL * InternalIterator; + + InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator; + + // CurrentNode can be NULL, but InitialNode cannot. + if ((InternalIterator == NULL) || + (InternalIterator->Mode != EAmlIteratorLinear) || + !IS_AML_NODE_VALID (InternalIterator->InitialNode) || + !IS_AML_NODE_VALID (InternalIterator->CurrentNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the next node according to the iteration mode. + InternalIterator->CurrentNode = AmlGetNextNode ( + InternalIterator->CurrentNode + ); + + if (NextNode != NULL) { + *NextNode = (AML_NODE_HEADER*)InternalIterator->CurrentNode; + } + return EFI_SUCCESS; +} + +/** Move the current node of the iterator to the previous node, + according to the iteration mode selected. + + If PrevNode is not NULL, return the previous node. + + @param [in] Iterator Pointer to an iterator. + @param [out] PrevNode If not NULL, updated to the previous node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlIteratorGetPreviousLinear ( + IN AML_TREE_ITERATOR * Iterator, + OUT AML_NODE_HEADER ** PrevNode + ) +{ + AML_TREE_ITERATOR_INTERNAL * InternalIterator; + + InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator; + + // CurrentNode can be NULL, but InitialNode cannot. + if ((InternalIterator == NULL) || + (InternalIterator->Mode != EAmlIteratorLinear) || + !IS_AML_NODE_VALID (InternalIterator->InitialNode) || + !IS_AML_NODE_VALID (InternalIterator->CurrentNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the previous node according to the iteration mode. + InternalIterator->CurrentNode = AmlGetPreviousNode ( + InternalIterator->CurrentNode + ); + if (PrevNode != NULL) { + *PrevNode = (AML_NODE_HEADER*)InternalIterator->CurrentNode; + } + return EFI_SUCCESS; +} + +/** Move the current node of the iterator to the next node, + according to the iteration mode selected. + + If NextNode is not NULL, return the next node. + + @param [in] Iterator Pointer to an iterator. + @param [out] NextNode If not NULL, updated to the next node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlIteratorGetNextBranch ( + IN AML_TREE_ITERATOR * Iterator, + OUT AML_NODE_HEADER ** NextNode + ) +{ + AML_TREE_ITERATOR_INTERNAL * InternalIterator; + AML_NODE_HEADER * Node; + + InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator; + + // CurrentNode can be NULL, but InitialNode cannot. + if ((InternalIterator == NULL) || + (InternalIterator->Mode != EAmlIteratorBranch) || + !IS_AML_NODE_VALID (InternalIterator->InitialNode) || + !IS_AML_NODE_VALID (InternalIterator->CurrentNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Node = AmlGetNextNode (InternalIterator->CurrentNode); + // Check whether NextNode is a sibling of InitialNode. + if (AmlGetParent (Node) == + AmlGetParent ((AML_NODE_HEADER*)InternalIterator->InitialNode)) { + Node = NULL; + } + + InternalIterator->CurrentNode = Node; + + if (NextNode != NULL) { + *NextNode = Node; + } + return EFI_SUCCESS; +} + +/** Move the current node of the iterator to the previous node, + according to the iteration mode selected. + + If PrevNode is not NULL, return the previous node. + + @param [in] Iterator Pointer to an iterator. + @param [out] PrevNode If not NULL, updated to the previous node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlIteratorGetPreviousBranch ( + IN AML_TREE_ITERATOR * Iterator, + OUT AML_NODE_HEADER ** PrevNode + ) +{ + AML_TREE_ITERATOR_INTERNAL * InternalIterator; + AML_NODE_HEADER * Node; + + InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator; + + // CurrentNode can be NULL, but InitialNode cannot. + if ((InternalIterator == NULL) || + (InternalIterator->Mode != EAmlIteratorBranch) || + !IS_AML_NODE_VALID (InternalIterator->InitialNode) || + !IS_AML_NODE_VALID (InternalIterator->CurrentNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Node = AmlGetPreviousNode (InternalIterator->CurrentNode); + // Check whether PreviousNode is a sibling of InitialNode. + if (AmlGetParent (Node) == + AmlGetParent ((AML_NODE_HEADER*)InternalIterator->InitialNode)) { + Node = NULL; + } + + InternalIterator->CurrentNode = Node; + + if (PrevNode != NULL) { + *PrevNode = Node; + } + return EFI_SUCCESS; +} + +/** Initialize an iterator. + + Note: The caller must call AmlDeleteIterator () to free the memory + allocated for the iterator. + + @param [in] Node Pointer to the node. + @param [in] IteratorMode Selected mode to traverse the tree. + @param [out] IteratorPtr Pointer holding the created iterator. + + @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 +AmlInitializeIterator ( + IN AML_NODE_HEADER * Node, + IN EAML_ITERATOR_MODE IteratorMode, + OUT AML_TREE_ITERATOR ** IteratorPtr + ) +{ + AML_TREE_ITERATOR_INTERNAL * InternalIterator; + + if (!IS_AML_NODE_VALID (Node) || + (IteratorMode <= EAmlIteratorUnknown) || + (IteratorMode >= EAmlIteratorModeMax) || + (IteratorPtr == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *IteratorPtr = NULL; + InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)AllocateZeroPool ( + sizeof ( + AML_TREE_ITERATOR_INTERNAL + ) + ); + if (InternalIterator == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + InternalIterator->InitialNode = Node; + InternalIterator->CurrentNode = Node; + InternalIterator->Mode = IteratorMode; + InternalIterator->Iterator.GetNode = AmlIteratorGetNode; + + switch (InternalIterator->Mode) { + case EAmlIteratorLinear: + { + InternalIterator->Iterator.GetNext = AmlIteratorGetNextLinear; + InternalIterator->Iterator.GetPrevious = AmlIteratorGetPreviousLinear; + break; + } + case EAmlIteratorBranch: + { + InternalIterator->Iterator.GetNext = AmlIteratorGetNextBranch; + InternalIterator->Iterator.GetPrevious = AmlIteratorGetPreviousBranch; + break; + } + default: + { + ASSERT (0); + FreePool (InternalIterator); + return EFI_INVALID_PARAMETER; + } + } // switch + + *IteratorPtr = &InternalIterator->Iterator; + + return EFI_SUCCESS; +} + +/** Delete an iterator. + + Note: The caller must have first initialized the iterator with the + AmlInitializeIterator () function. + + @param [in] Iterator Pointer to an iterator. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeleteIterator ( + IN AML_TREE_ITERATOR * Iterator + ) +{ + if (Iterator == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + FreePool (Iterator); + + return EFI_SUCCESS; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h new file mode 100644 index 000000000..d96eecab9 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h @@ -0,0 +1,220 @@ +/** @file + AML Iterator. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_ITERATOR_H_ +#define AML_ITERATOR_H_ + +/* This header file does not include internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. The node definitions + must be included by the caller file. The function prototypes must + only expose AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, etc. node + definitions. + This allows to keep the functions defined here both internal and + potentially external. If necessary, any function of this file can + be exposed externally. + The Api folder is internal to the AmlLib, but should only use these + functions. They provide a "safe" way to interact with the AmlLib. +*/ + +/** + @defgroup IteratorLibrary Iterator library + @ingroup NavigationApis + @{ + The iterator library allow to navigate in the AML tree using an iterator. + It is possible to initialize/delete an iterator. + + This iterator can progress in the tree by different orders: + - Linear progression: Iterate following the AML bytestream order + (depth first). + - Branch progression: Iterate following the AML bytestream order + (depth first), but stop iterating at the + end of the branch. + + An iterator has the following features: + - GetNode: Get the current node pointed by the iterator. + - GetNext: Move the current node of the iterator to the next + node, according to the iteration mode selected. + - GetPrevious: Move the current node of the iterator to the previous + node, according to the iteration mode selected. + @} +*/ + +/** + @defgroup IteratorApis Iterator APIs + @ingroup IteratorLibrary + @{ + Iterator APIs defines the action that can be done on an iterator: + - Initialization; + - Deletion; + - Getting the node currently pointed by the iterator; + - Moving to the next node; + - Moving to the previous node. + @} +*/ + +/** + @defgroup IteratorStructures Iterator structures + @ingroup IteratorLibrary + @{ + Iterator structures define the enum/define values and structures related + to iterators. + @} +*/ + +/** Iterator mode. + + Modes to choose how the iterator is progressing in the tree. + A + \-B <- Iterator initialized with this node. + | \-C + | | \-D + | \-E + | \-F + | \-G + \-H + \-I + + @ingroup IteratorStructures +*/ +typedef enum EAmlIteratorMode { + EAmlIteratorUnknown, ///< Unknown/Invalid AML IteratorMode + EAmlIteratorLinear, ///< Iterate following the AML bytestream order + /// (depth first). + /// The order followed by the iterator would be: + /// B, C, D, E, F, G, H, I, NULL. + EAmlIteratorBranch, ///< Iterate through the node of a branch. + /// The iteration follows the AML bytestream + /// order but within the branch B. + /// The order followed by the iterator would be: + /// B, C, D, E, F, G, NULL. + EAmlIteratorModeMax ///< Max enum. +} EAML_ITERATOR_MODE; + +/** Iterator. + + Allows to traverse the tree in different orders. + + @ingroup IteratorStructures +*/ +typedef struct AmlTreeIterator AML_TREE_ITERATOR; + +/** Function pointer to a get the current node of the iterator. + + @ingroup IteratorApis + + @param [in] Iterator Pointer to an iterator. + @param [out] OutNode Pointer holding the current node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +*/ +typedef +EFI_STATUS +(EFIAPI * EDKII_AML_TREE_ITERATOR_GET_NODE) ( + IN AML_TREE_ITERATOR * Iterator, + OUT AML_NODE_HANDLE * OutNode + ); + +/** Function pointer to move the current node of the iterator to the + next node, according to the iteration mode selected. + + If NextNode is not NULL, return the next node. + + @ingroup IteratorApis + + @param [in] Iterator Pointer to an iterator. + @param [out] NextNode If not NULL, updated to the next node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +*/ +typedef +EFI_STATUS +(EFIAPI * EDKII_AML_TREE_ITERATOR_GET_NEXT) ( + IN AML_TREE_ITERATOR * Iterator, + OUT AML_NODE_HANDLE * NextNode + ); + +/** Function pointer to move the current node of the iterator to the + previous node, according to the iteration mode selected. + + If PrevNode is not NULL, return the previous node. + + @ingroup IteratorApis + + @param [in] Iterator Pointer to an iterator. + @param [out] PrevNode If not NULL, updated to the previous node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +*/ +typedef +EFI_STATUS +(EFIAPI * EDKII_AML_TREE_ITERATOR_GET_PREVIOUS) ( + IN AML_TREE_ITERATOR * Iterator, + OUT AML_NODE_HANDLE * PrevNode + ); + +/** Iterator structure to traverse the tree. + + @ingroup IteratorStructures +*/ +typedef struct AmlTreeIterator { + /// Get the current node of the iterator. + EDKII_AML_TREE_ITERATOR_GET_NODE GetNode; + + /// Update the current node of the iterator with the next node. + EDKII_AML_TREE_ITERATOR_GET_NEXT GetNext; + + /// Update the current node of the iterator with the previous node. + EDKII_AML_TREE_ITERATOR_GET_PREVIOUS GetPrevious; +} AML_TREE_ITERATOR; + + +/** Initialize an iterator. + + Note: The caller must call AmlDeleteIterator () to free the memory + allocated for the iterator. + + @ingroup IteratorApis + + @param [in] Node Pointer to the node. + @param [in] IteratorMode Selected mode to traverse the tree. + @param [out] IteratorPtr Pointer holding the created iterator. + + @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 +AmlInitializeIterator ( + IN AML_NODE_HANDLE Node, + IN EAML_ITERATOR_MODE IteratorMode, + OUT AML_TREE_ITERATOR ** IteratorPtr + ); + +/** Delete an iterator. + + Note: The caller must have first initialized the iterator with the + AmlInitializeIterator () function. + + @ingroup IteratorApis + + @param [in] Iterator Pointer to an iterator. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeleteIterator ( + IN AML_TREE_ITERATOR * Iterator + ); + +#endif // AML_ITERATOR_H_ diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c new file mode 100644 index 000000000..9d0c794db --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c @@ -0,0 +1,548 @@ +/** @file + AML Tree Traversal. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include + +/** Get the sibling node among the nodes being in + the same variable argument list. + + (ParentNode) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(VarArgNode)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Node must be in a variable list of arguments. + Traversal Order: VarArgNode, f, g, NULL + + @ingroup CoreNavigationApis + + @param [in] VarArgNode Pointer to a node. + Must be in a variable list of arguments. + + @return The next node after VarArgNode in the variable list of arguments. + Return NULL if + - VarArgNode is the last node of the list, or + - VarArgNode is not part of a variable list of arguments. +**/ +AML_NODE_HEADER * +EFIAPI +AmlGetSiblingVariableArgument ( + IN AML_NODE_HEADER * VarArgNode + ) +{ + EAML_PARSE_INDEX Index; + AML_NODE_HEADER * ParentNode; + + // VarArgNode must be an object node or a data node, + // and be in a variable list of arguments. + if ((!IS_AML_OBJECT_NODE (VarArgNode) && + !IS_AML_DATA_NODE (VarArgNode)) || + AmlIsNodeFixedArgument (VarArgNode, &Index)) { + ASSERT (0); + return NULL; + } + + ParentNode = AmlGetParent (VarArgNode); + if (!IS_AML_NODE_VALID (ParentNode)) { + ASSERT (0); + return NULL; + } + + return AmlGetNextVariableArgument (ParentNode, VarArgNode); +} + +/** Get the next variable argument. + + (Node) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(e)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Traversal Order: e, f, g, NULL + + @param [in] Node Pointer to a Root node or Object Node. + @param [in] CurrVarArg Pointer to the Current Variable Argument. + + @return The node after the CurrVarArg in the variable list of arguments. + If CurrVarArg is NULL, return the first node of the + variable argument list. + Return NULL if + - CurrVarArg is the last node of the list, or + - Node does not have a variable list of arguments. +**/ +AML_NODE_HEADER * +EFIAPI +AmlGetNextVariableArgument ( + IN AML_NODE_HEADER * Node, + IN AML_NODE_HEADER * CurrVarArg + ) +{ + CONST LIST_ENTRY * StartLink; + CONST LIST_ENTRY * NextLink; + + // Node must be a RootNode or an Object Node + // and the CurrVarArg must not be a Root Node. + if ((!IS_AML_ROOT_NODE (Node) && + !IS_AML_OBJECT_NODE (Node)) || + ((CurrVarArg != NULL) && + (!IS_AML_OBJECT_NODE (CurrVarArg) && + !IS_AML_DATA_NODE (CurrVarArg)))) { + ASSERT (0); + return NULL; + } + + StartLink = AmlNodeGetVariableArgList (Node); + if (StartLink == NULL) { + return NULL; + } + + // Get the first child of the variable list of arguments. + if (CurrVarArg == NULL) { + NextLink = StartLink->ForwardLink; + if (NextLink != StartLink) { + return (AML_NODE_HEADER*)NextLink; + } + // List is empty. + return NULL; + } + + // Check if CurrVarArg is in the VariableArgument List. + if (!IsNodeInList (StartLink, &CurrVarArg->Link)) { + ASSERT (0); + return NULL; + } + + // Get the node following the CurrVarArg. + NextLink = CurrVarArg->Link.ForwardLink; + if (NextLink != StartLink) { + return (AML_NODE_HEADER*)NextLink; + } + + // End of the list has been reached. + return NULL; +} + +/** Get the previous variable argument. + + (Node) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(e)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Traversal Order: g, f, e, NULL + + @param [in] Node Pointer to a root node or an object node. + @param [in] CurrVarArg Pointer to the Current Variable Argument. + + @return The node before the CurrVarArg in the variable list of + arguments. + If CurrVarArg is NULL, return the last node of the + variable list of arguments. + Return NULL if: + - CurrVarArg is the first node of the list, or + - Node doesn't have a variable list of arguments. +**/ +AML_NODE_HEADER * +EFIAPI +AmlGetPreviousVariableArgument ( + IN AML_NODE_HEADER * Node, + IN AML_NODE_HEADER * CurrVarArg + ) +{ + CONST LIST_ENTRY * StartLink; + CONST LIST_ENTRY * PreviousLink; + + // Node must be a RootNode or an Object Node + // and the CurrVarArg must not be a Root Node. + if ((!IS_AML_ROOT_NODE (Node) && + !IS_AML_OBJECT_NODE (Node)) || + ((CurrVarArg != NULL) && + (!IS_AML_OBJECT_NODE (CurrVarArg) && + !IS_AML_DATA_NODE (CurrVarArg)))) { + ASSERT (0); + return NULL; + } + + StartLink = AmlNodeGetVariableArgList (Node); + if (StartLink == NULL) { + return NULL; + } + + // Get the last child of the variable list of arguments. + if (CurrVarArg == NULL) { + PreviousLink = StartLink->BackLink; + if (PreviousLink != StartLink) { + return (AML_NODE_HEADER*)PreviousLink; + } + // List is empty. + return NULL; + } + + // Check if CurrVarArg is in the VariableArgument List. + if (!IsNodeInList (StartLink, &CurrVarArg->Link)) { + ASSERT (0); + return NULL; + } + + // Get the node before the CurrVarArg. + PreviousLink = CurrVarArg->Link.BackLink; + if (PreviousLink != StartLink) { + return (AML_NODE_HEADER*)PreviousLink; + } + + // We have reached the beginning of the list. + return NULL; +} + +/** Get the next sibling node among the children of the input Node. + + This function traverses the FixedArguments followed by the + VariableArguments at the same level in the hierarchy. + + Fixed arguments are before variable arguments. + + (Node) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(e)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Traversal Order: a, b, c, d, e, f, g, NULL + + + @param [in] Node Pointer to a root node or an object node. + @param [in] ChildNode Get the node after the ChildNode. + + @return The node after the ChildNode among the children of the input Node. + - If ChildNode is NULL, return the first available node among + the fixed argument list then variable list of arguments; + - If ChildNode is the last node of the fixed argument list, + return the first argument of the variable list of arguments; + - If ChildNode is the last node of the variable list of arguments, + return NULL. + +**/ +AML_NODE_HEADER * +EFIAPI +AmlGetNextSibling ( + IN CONST AML_NODE_HEADER * Node, + IN CONST AML_NODE_HEADER * ChildNode + ) +{ + EAML_PARSE_INDEX Index; + AML_NODE_HEADER * CandidateNode; + + // Node must be a RootNode or an Object Node + // and the CurrVarArg must not be a Root Node. + if ((!IS_AML_ROOT_NODE (Node) && + !IS_AML_OBJECT_NODE (Node)) || + ((ChildNode != NULL) && + (!IS_AML_OBJECT_NODE (ChildNode) && + !IS_AML_DATA_NODE (ChildNode)))) { + ASSERT (0); + return NULL; + } + + if (IS_AML_OBJECT_NODE (Node)) { + if (ChildNode == NULL) { + // Get the fixed argument at index 0 of the ChildNode. + CandidateNode = AmlGetFixedArgument ( + (AML_OBJECT_NODE*)Node, + EAmlParseIndexTerm0 + ); + if (CandidateNode != NULL) { + return CandidateNode; + } + } else { + // (ChildNode != NULL) + if (AmlIsNodeFixedArgument (ChildNode, &Index)) { + // Increment index to point to the next fixed argument. + Index++; + // The node is part of the list of fixed arguments. + if (Index == (EAML_PARSE_INDEX)AmlGetFixedArgumentCount ( + (AML_OBJECT_NODE*)Node) + ) { + // It is at the last argument of the fixed argument list. + // Get the first argument of the variable list of arguments. + ChildNode = NULL; + } else { + // Else return the next node in the list of fixed arguments. + return AmlGetFixedArgument ((AML_OBJECT_NODE*)Node, Index); + } + } + } + } // IS_AML_OBJECT_NODE (Node) + + // Else, get the next node in the variable list of arguments. + return AmlGetNextVariableArgument ( + (AML_NODE_HEADER*)Node, + (AML_NODE_HEADER*)ChildNode + ); +} + +/** Get the previous sibling node among the children of the input Node. + + This function traverses the FixedArguments followed by the + VariableArguments at the same level in the hierarchy. + + Fixed arguments are before variable arguments. + + (Node) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(e)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Traversal Order: g, f, e, d, c, b, a, NULL + + @param [in] Node The node to get the fixed argument from. + @param [in] ChildNode Get the node before the ChildNode. + + @return The node before the ChildNode among the children of the input Node. + - If ChildNode is NULL, return the last available node among + the variable list of arguments then fixed argument list; + - If ChildNode is the first node of the variable list of arguments, + return the last argument of the fixed argument list; + - If ChildNode is the first node of the fixed argument list, + return NULL. +**/ +AML_NODE_HEADER * +EFIAPI +AmlGetPreviousSibling ( + IN CONST AML_NODE_HEADER * Node, + IN CONST AML_NODE_HEADER * ChildNode + ) +{ + EAML_PARSE_INDEX Index; + EAML_PARSE_INDEX MaxIndex; + + AML_NODE_HEADER * CandidateNode; + + // Node must be a Root Node or an Object Node + // and the ChildNode must not be a Root Node. + if ((!IS_AML_ROOT_NODE (Node) && + !IS_AML_OBJECT_NODE (Node)) || + ((ChildNode != NULL) && + (!IS_AML_OBJECT_NODE (ChildNode) && + !IS_AML_DATA_NODE (ChildNode)))) { + ASSERT (0); + return NULL; + } + + MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount ( + (AML_OBJECT_NODE*)Node + ); + + // Get the last variable argument if no ChildNode. + // Otherwise the fixed argument list is checked first. + if ((ChildNode != NULL) && + IS_AML_OBJECT_NODE (Node) && + (MaxIndex != EAmlParseIndexTerm0)) { + if (AmlIsNodeFixedArgument (ChildNode, &Index)) { + // The node is part of the list of fixed arguments. + if (Index == EAmlParseIndexTerm0) { + // The node is the first fixed argument, return NULL. + return NULL; + } else { + // Return the previous node in the fixed argument list. + return AmlGetFixedArgument ( + (AML_OBJECT_NODE*)Node, + (EAML_PARSE_INDEX)(Index - 1) + ); + } + } + } + + // ChildNode is in the variable list of arguments. + CandidateNode = AmlGetPreviousVariableArgument ( + (AML_NODE_HEADER*)Node, + (AML_NODE_HEADER*)ChildNode + ); + if (CandidateNode != NULL) { + if (!IS_AML_NODE_VALID (CandidateNode)) { + ASSERT (0); + return NULL; + } + // A Node has been found + return CandidateNode; + } else if (MaxIndex != EAmlParseIndexTerm0) { + // ChildNode was the first node of the variable list of arguments. + return AmlGetFixedArgument ( + (AML_OBJECT_NODE*)Node, + (EAML_PARSE_INDEX)(MaxIndex - 1) + ); + } else { + // No fixed arguments or variable arguments. + return NULL; + } +} + +/** Iterate through the nodes in the same order as the AML bytestream. + + The iteration is similar to a depth-first path. + + (Node) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(e)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Traversal Order: a, b, i, c, d, e, h, f, g, NULL + Note: The branch i and h will be traversed if it has any children. + + @param [in] Node Pointer to a node. + + @return The next node in the AML bytestream order. + Return NULL if Node is the Node corresponding to the last + bytecode of the tree. +**/ +AML_NODE_HEADER * +EFIAPI +AmlGetNextNode ( + IN CONST AML_NODE_HEADER * Node + ) +{ + AML_NODE_HEADER * ParentNode; + AML_NODE_HEADER * CandidateNode; + + if (!IS_AML_NODE_VALID (Node)) { + ASSERT (0); + return NULL; + } + + if (IS_AML_ROOT_NODE (Node) || IS_AML_OBJECT_NODE (Node)) { + // The node has children. Get the first child. + CandidateNode = AmlGetNextSibling (Node, NULL); + if (CandidateNode != NULL) { + if (!IS_AML_NODE_VALID (CandidateNode)) { + ASSERT (0); + return NULL; + } + // A Node has been found + return CandidateNode; + } else if (IS_AML_ROOT_NODE (Node)) { + // The node is the root node and it doesn't have children. + return NULL; + } + } + + // We have traversed the current branch, go to the parent node + // and start traversing the next branch. + // Keep going up the tree until you reach the root node. + while (1) { + if (IS_AML_ROOT_NODE (Node)) { + // This is the last node of the tree. + return NULL; + } + + ParentNode = AmlGetParent ((AML_NODE_HEADER*)Node); + if (!IS_AML_NODE_VALID (ParentNode)) { + ASSERT (0); + return NULL; + } + + CandidateNode = AmlGetNextSibling (ParentNode, Node); + if (CandidateNode != NULL) { + if (!IS_AML_NODE_VALID (CandidateNode)) { + ASSERT (0); + return NULL; + } + // A Node has been found + return CandidateNode; + } + + Node = ParentNode; + } // while + + return NULL; +} + +/** Iterate through the nodes in the reverse order of the AML bytestream. + + The iteration is similar to a depth-first path, + but done in a reverse order. + + (Node) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(e)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Traversal Order: g, f, h, e, d, c, i, b, a, NULL + Note: The branch i and h will be traversed if it has any children. + + @param [in] Node Pointer to a node. + + @return The previous node in the AML bytestream order. + Return NULL if Node is the Node corresponding to the last + bytecode of the tree. +**/ +AML_NODE_HEADER * +EFIAPI +AmlGetPreviousNode ( + IN CONST AML_NODE_HEADER * Node + ) +{ + AML_NODE_HEADER * ParentNode; + AML_NODE_HEADER * CandidateNode; + AML_NODE_HEADER * PreviousNode; + + if (!IS_AML_NODE_VALID (Node)) { + ASSERT (0); + return NULL; + } + + while (1) { + + if (IS_AML_ROOT_NODE (Node)) { + // This is the root node. + return NULL; + } + + ParentNode = AmlGetParent ((AML_NODE_HEADER*)Node); + CandidateNode = AmlGetPreviousSibling (ParentNode, Node); + + if (CandidateNode == NULL) { + // Node is the first child of its parent. + return ParentNode; + } else if (IS_AML_DATA_NODE (CandidateNode)) { + // CandidateNode is a data node, thus it has no children. + return CandidateNode; + } else if (IS_AML_OBJECT_NODE (CandidateNode)) { + // Get the previous node in the list of children of ParentNode, + // then get the last child of this node. + // If this node has children, get its last child, etc. + while (1) { + PreviousNode = CandidateNode; + CandidateNode = AmlGetPreviousSibling (PreviousNode, NULL); + if (CandidateNode == NULL) { + return PreviousNode; + } else if (IS_AML_DATA_NODE (CandidateNode)) { + return CandidateNode; + } + } // while + + } else { + ASSERT (0); + return NULL; + } + } // while +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h new file mode 100644 index 000000000..a4980b716 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h @@ -0,0 +1,138 @@ +/** @file + AML Tree Traversal. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_TREE_TRAVERSAL_H_ +#define AML_TREE_TRAVERSAL_H_ + +#include + +/** Get the next sibling node among the children of the input Node. + + This function traverses the FixedArguments followed by the + VariableArguments at the same level in the hierarchy. + + Fixed arguments are before variable arguments. + + (Node) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(e)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Traversal Order: a, b, c, d, e, f, g, NULL + + + @param [in] Node Pointer to a root node or an object node. + @param [in] ChildNode Get the node after the ChildNode. + + @return The node after the ChildNode among the children of the input Node. + - If ChildNode is NULL, return the first available node among + the fixed argument list then variable list of arguments; + - If ChildNode is the last node of the fixed argument list, + return the first argument of the variable list of arguments; + - If ChildNode is the last node of the variable list of arguments, + return NULL. + +**/ +AML_NODE_HEADER * +EFIAPI +AmlGetNextSibling ( + IN CONST AML_NODE_HEADER * Node, + IN CONST AML_NODE_HEADER * ChildNode + ); + +/** Get the previous sibling node among the children of the input Node. + + This function traverses the FixedArguments followed by the + VariableArguments at the same level in the hierarchy. + + Fixed arguments are before variable arguments. + + (Node) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(e)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Traversal Order: g, f, e, d, c, b, a, NULL + + @param [in] Node The node to get the fixed argument from. + @param [in] ChildNode Get the node before the ChildNode. + + @return The node before the ChildNode among the children of the input Node. + - If ChildNode is NULL, return the last available node among + the variable list of arguments then fixed argument list; + - If ChildNode is the first node of the variable list of arguments, + return the last argument of the fixed argument list; + - If ChildNode is the first node of the fixed argument list, + return NULL. +**/ +AML_NODE_HEADER * +EFIAPI +AmlGetPreviousSibling ( + IN CONST AML_NODE_HEADER * Node, + IN CONST AML_NODE_HEADER * ChildNode + ); + +/** Iterate through the nodes in the same order as the AML bytestream. + + The iteration is similar to a depth-first path. + + (Node) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(e)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Traversal Order: a, b, i, c, d, e, h, f, g, NULL + Note: The branch i and h will be traversed if it has any children. + + @param [in] Node Pointer to a node. + + @return The next node in the AML bytestream order. + Return NULL if Node is the Node corresponding to the last + bytecode of the tree. +**/ +AML_NODE_HEADER * +EFIAPI +AmlGetNextNode ( + IN CONST AML_NODE_HEADER * Node + ); + +/** Iterate through the nodes in the reverse order of the AML bytestream. + + The iteration is similar to a depth-first path, + but done in a reverse order. + + (Node) /-i # Child of fixed argument b + \ / + |- [a][b][c][d] # Fixed Arguments + |- {(e)->(f)->(g)} # Variable Arguments + \ + \-h # Child of variable argument e + + Traversal Order: g, f, h, e, d, c, i, b, a, NULL + Note: The branch i and h will be traversed if it has any children. + + @param [in] Node Pointer to a node. + + @return The previous node in the AML bytestream order. + Return NULL if Node is the Node corresponding to the last + bytecode of the tree. +**/ +AML_NODE_HEADER * +EFIAPI +AmlGetPreviousNode ( + IN CONST AML_NODE_HEADER * Node + ); + +#endif // AML_TREE_TRAVERSAL_H_ + diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c new file mode 100644 index 000000000..7ebd08f94 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c @@ -0,0 +1,906 @@ +/** @file + AML Utility. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include + +/** This function computes and updates the ACPI table checksum. + + @param [in] AcpiTable Pointer to an Acpi table. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AcpiPlatformChecksum ( + IN EFI_ACPI_DESCRIPTION_HEADER * AcpiTable + ) +{ + UINT8 * Ptr; + UINT8 Sum; + UINT32 Size; + + if (AcpiTable == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Ptr = (UINT8*)AcpiTable; + Size = AcpiTable->Length; + Sum = 0; + + // Set the checksum field to 0 first. + AcpiTable->Checksum = 0; + + // Compute the checksum. + while ((Size--) != 0) { + Sum = (UINT8)(Sum + (*Ptr++)); + } + + // Set the checksum. + AcpiTable->Checksum = (UINT8)(0xFF - Sum + 1); + + return EFI_SUCCESS; +} + +/** A callback function that computes the size of a Node and adds it to the + Size pointer stored in the Context. + Calling this function on the root node will compute the total size of the + AML bytestream. + + @param [in] Node Node to compute the size. + @param [in, out] Context Pointer holding the computed size. + (UINT32 *) Context. + @param [in, out] Status Pointer holding: + - At entry, the Status returned by the + last call to this exact function during + the enumeration; + - At exit, he 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 +AmlComputeSizeCallback ( + IN AML_NODE_HEADER * Node, + IN OUT VOID * Context, + IN OUT EFI_STATUS * Status OPTIONAL + ) +{ + UINT32 Size; + EAML_PARSE_INDEX IndexPtr; + CONST AML_OBJECT_NODE * ParentNode; + + if (!IS_AML_NODE_VALID (Node) || + (Context == NULL)) { + ASSERT (0); + if (Status != NULL) { + *Status = EFI_INVALID_PARAMETER; + } + return FALSE; + } + + // Ignore the second fixed argument of method invocation nodes + // as the information stored there (the argument count) is not in the + // ACPI specification. + ParentNode = (CONST AML_OBJECT_NODE*)AmlGetParent (Node); + if (IS_AML_OBJECT_NODE (ParentNode) && + AmlNodeCompareOpCode (ParentNode, AML_METHOD_INVOC_OP, 0) && + AmlIsNodeFixedArgument (Node, &IndexPtr)) { + if (IndexPtr == EAmlParseIndexTerm1) { + if (Status != NULL) { + *Status = EFI_SUCCESS; + } + return TRUE; + } + } + + Size = *((UINT32*)Context); + + if (IS_AML_DATA_NODE (Node)) { + Size += ((AML_DATA_NODE*)Node)->Size; + } else if (IS_AML_OBJECT_NODE (Node) && + !AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)Node, + AML_IS_PSEUDO_OPCODE)) { + // Ignore pseudo-opcodes as they are not part of the + // ACPI specification. + + Size += (((AML_OBJECT_NODE*)Node)->AmlByteEncoding->OpCode == + AML_EXT_OP) ? 2 : 1; + + // Add the size of the PkgLen. + if (AmlNodeHasAttribute ( + (AML_OBJECT_NODE*)Node, + AML_HAS_PKG_LENGTH)) { + Size += AmlComputePkgLengthWidth (((AML_OBJECT_NODE*)Node)->PkgLen); + } + } + + // Check for overflow. + // The root node has a null size, thus the strict comparison. + if (*((UINT32*)Context) > Size) { + ASSERT (0); + *Status = EFI_INVALID_PARAMETER; + return FALSE; + } + + *((UINT32*)Context) = Size; + + if (Status != NULL) { + *Status = EFI_SUCCESS; + } + + return TRUE; +} + +/** Compute the size of a tree/sub-tree. + + @param [in] Node Node to compute the size. + @param [in, out] Size Pointer holding the computed size. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlComputeSize ( + IN CONST AML_NODE_HEADER * Node, + IN OUT UINT32 * Size + ) +{ + EFI_STATUS Status; + + if (!IS_AML_NODE_VALID (Node) || + (Size == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *Size = 0; + + AmlEnumTree ( + (AML_NODE_HEADER*)Node, + AmlComputeSizeCallback, + (VOID*)Size, + &Status + ); + + return Status; +} + +/** Get the value contained in an integer node. + + @param [in] Node Pointer to an integer node. + Must be an object node. + @param [out] Value Value contained in the integer node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlNodeGetIntegerValue ( + IN AML_OBJECT_NODE * Node, + OUT UINT64 * Value + ) +{ + AML_DATA_NODE * DataNode; + + if ((!IsIntegerNode (Node) && + !IsSpecialIntegerNode (Node)) || + (Value == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // For ZeroOp and OneOp, there is no data node. + if (IsSpecialIntegerNode (Node)) { + if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) { + *Value = 0; + } else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) { + *Value = 1; + } else { + // OnesOp cannot be handled: it represents a maximum value. + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; + } + + // For integer nodes, the value is in the first fixed argument. + DataNode = (AML_DATA_NODE*)Node->FixedArgs[EAmlParseIndexTerm0]; + if (!IS_AML_DATA_NODE (DataNode) || + (DataNode->DataType != EAmlNodeDataTypeUInt)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + switch (DataNode->Size) { + case 1: + { + *Value = *((UINT8*)(DataNode->Buffer)); + break; + } + case 2: + { + *Value = *((UINT16*)(DataNode->Buffer)); + break; + } + case 4: + { + *Value = *((UINT32*)(DataNode->Buffer)); + break; + } + case 8: + { + *Value = *((UINT64*)(DataNode->Buffer)); + break; + } + default: + { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } // switch + + return EFI_SUCCESS; +} + +/** Replace a Zero (AML_ZERO_OP) or One (AML_ONE_OP) object node + with a byte integer (AML_BYTE_PREFIX) object node having the same value. + + @param [in] Node Pointer to an integer node. + Must be an object node having ZeroOp or OneOp. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlUnwindSpecialInteger ( + IN AML_OBJECT_NODE * Node + ) +{ + EFI_STATUS Status; + + AML_DATA_NODE * NewDataNode; + UINT8 Value; + CONST AML_BYTE_ENCODING * ByteEncoding; + + if (!IsSpecialIntegerNode (Node)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Find the value. + if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) { + Value = 0; + } else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) { + Value = 1; + } else { + // OnesOp cannot be handled: it represents a maximum value. + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlCreateDataNode ( + EAmlNodeDataTypeUInt, + &Value, + sizeof (UINT8), + (AML_DATA_NODE**)&NewDataNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Change the encoding of the special node to a ByteOp encoding. + ByteEncoding = AmlGetByteEncodingByOpCode (AML_BYTE_PREFIX, 0); + if (ByteEncoding == NULL) { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + // Update the ByteEncoding from ZERO_OP/ONE_OP to AML_BYTE_PREFIX. + Node->AmlByteEncoding = ByteEncoding; + + // Add the data node as the first fixed argument of the ByteOp object. + Status = AmlSetFixedArgument ( + (AML_OBJECT_NODE*)Node, + EAmlParseIndexTerm0, + (AML_NODE_HEADER*)NewDataNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + return Status; + +error_handler: + AmlDeleteTree ((AML_NODE_HEADER*)NewDataNode); + return Status; +} + +/** Set the value contained in an integer node. + + The OpCode is updated accordingly to the new value + (e.g.: If the original value was a UINT8 value, then the OpCode + would be AML_BYTE_PREFIX. If it the new value is a UINT16 + value then the OpCode will be updated to AML_WORD_PREFIX). + + @param [in] Node Pointer to an integer node. + Must be an object node. + @param [in] NewValue New value to write in the integer node. + @param [out] ValueWidthDiff Difference in number of bytes used to store + the new value. + Can be negative. + + @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 +AmlNodeSetIntegerValue ( + IN AML_OBJECT_NODE * Node, + IN UINT64 NewValue, + OUT INT8 * ValueWidthDiff + ) +{ + EFI_STATUS Status; + AML_DATA_NODE * DataNode; + + UINT8 NewOpCode; + UINT8 NumberOfBytes; + + if ((!IsIntegerNode (Node) && + !IsSpecialIntegerNode (Node)) || + (ValueWidthDiff == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *ValueWidthDiff = 0; + // For ZeroOp and OneOp, there is no data node. + // Thus the object node is converted to a byte object node holding 0 or 1. + if (IsSpecialIntegerNode (Node)) { + switch (NewValue) { + case AML_ZERO_OP: + Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ZERO_OP, 0); + return EFI_SUCCESS; + case AML_ONE_OP: + Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ONE_OP, 0); + return EFI_SUCCESS; + default: + { + Status = AmlUnwindSpecialInteger (Node); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + // The AmlUnwindSpecialInteger functions converts a special integer + // node to a UInt8/Byte data node. Thus, the size increments by one: + // special integer are encoded as one byte (the opcode only) while byte + // integers are encoded as two bytes (the opcode + the value). + *ValueWidthDiff += sizeof (UINT8); + } + } // switch + } // IsSpecialIntegerNode (Node) + + // For integer nodes, the value is in the first fixed argument. + DataNode = (AML_DATA_NODE*)Node->FixedArgs[EAmlParseIndexTerm0]; + if (!IS_AML_DATA_NODE (DataNode) || + (DataNode->DataType != EAmlNodeDataTypeUInt)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // The value can be encoded with a special 0 or 1 OpCode. + // The AML_ONES_OP is not handled. + if (NewValue <= 1) { + NewOpCode = (NewValue == 0) ? AML_ZERO_OP : AML_ONE_OP; + Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0); + + // The value is encoded with a AML_ZERO_OP or AML_ONE_OP. + // This means there is no need for a DataNode containing the value. + // The change in size is equal to the size of the DataNode's buffer. + *ValueWidthDiff = -((INT8)DataNode->Size); + + // Detach and free the DataNode containing the integer value. + DataNode->NodeHeader.Parent = NULL; + Node->FixedArgs[EAmlParseIndexTerm0] = NULL; + Status = AmlDeleteNode ((AML_NODE_HEADER*)DataNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + return EFI_SUCCESS; + } + + // Check the number of bits needed to represent the value. + if (NewValue > MAX_UINT32) { + // Value is 64 bits. + NewOpCode = AML_QWORD_PREFIX; + NumberOfBytes = 8; + } else if (NewValue > MAX_UINT16) { + // Value is 32 bits. + NewOpCode = AML_DWORD_PREFIX; + NumberOfBytes = 4; + } else if (NewValue > MAX_UINT8) { + // Value is 16 bits. + NewOpCode = AML_WORD_PREFIX; + NumberOfBytes = 2; + } else { + // Value is 8 bits. + NewOpCode = AML_BYTE_PREFIX; + NumberOfBytes = 1; + } + + *ValueWidthDiff += (INT8)(NumberOfBytes - DataNode->Size); + + // Update the ByteEncoding as it may have changed between [8 .. 64] bits. + Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0); + if (Node->AmlByteEncoding == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Free the old DataNode buffer and allocate a buffer with the right size + // to store the new data. + if (*ValueWidthDiff != 0) { + FreePool (DataNode->Buffer); + DataNode->Buffer = AllocateZeroPool (NumberOfBytes); + if (DataNode->Buffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + DataNode->Size = NumberOfBytes; + } + + // Write the new value. + CopyMem (DataNode->Buffer, &NewValue, NumberOfBytes); + + return EFI_SUCCESS; +} + +/** Increment/decrement the value contained in the IntegerNode. + + @param [in] IntegerNode Pointer to an object node containing + an integer. + @param [in] IsIncrement Choose the operation to do: + - TRUE: Increment the Node's size and + the Node's count; + - FALSE: Decrement the Node's size and + the Node's count. + @param [in] Diff Value to add/subtract to the integer. + @param [out] ValueWidthDiff When modifying the integer, it can be + promoted/demoted, e.g. from UINT8 to UINT16. + Stores the change in width. + Can be negative. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlNodeUpdateIntegerValue ( + IN AML_OBJECT_NODE * IntegerNode, + IN BOOLEAN IsIncrement, + IN UINT64 Diff, + OUT INT8 * ValueWidthDiff + ) +{ + EFI_STATUS Status; + UINT64 Value; + + if (ValueWidthDiff == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the current value. + // Checks on the IntegerNode are done in the call. + Status = AmlNodeGetIntegerValue (IntegerNode, &Value); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Check for UINT64 over/underflow. + if ((IsIncrement && (Value > (MAX_UINT64 - Diff))) || + (!IsIncrement && (Value < Diff))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Compute the new value. + if (IsIncrement) { + Value += Diff; + } else { + Value -= Diff; + } + + Status = AmlNodeSetIntegerValue ( + IntegerNode, + Value, + ValueWidthDiff + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Propagate the size information up the tree. + + The length of the ACPI table is updated in the RootNode, + but not the checksum. + + @param [in] Node Pointer to a node. + Must be a root node or an object node. + @param [in] IsIncrement Choose the operation to do: + - TRUE: Increment the Node's size and + the Node's count; + - FALSE: Decrement the Node's size and + the Node's count. + @param [in] Diff Value to add/subtract to the Node's size. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlPropagateSize ( + IN AML_NODE_HEADER * Node, + IN BOOLEAN IsIncrement, + IN UINT32 * Diff + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE * ObjectNode; + AML_NODE_HEADER * ParentNode; + + UINT32 Value; + UINT32 InitialPkgLenWidth; + UINT32 NewPkgLenWidth; + UINT32 ReComputedPkgLenWidth; + INT8 FieldWidthChange; + + if (!IS_AML_OBJECT_NODE (Node) && + !IS_AML_ROOT_NODE (Node)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (IS_AML_OBJECT_NODE (Node)) { + ObjectNode = (AML_OBJECT_NODE*)Node; + + // For BufferOp, the buffer size is stored in BufferSize. Therefore, + // BufferOp needs special handling to update the BufferSize. + // BufferSize must be updated before the PkgLen to accommodate any + // increment resulting from the update of the BufferSize. + // DefBuffer := BufferOp PkgLength BufferSize ByteList + // BufferOp := 0x11 + // BufferSize := TermArg => Integer + if (AmlNodeCompareOpCode (ObjectNode, AML_BUFFER_OP, 0)) { + // First fixed argument of BufferOp is an integer (BufferSize) + // (can be a BYTE, WORD, DWORD or QWORD). + // BufferSize is an object node. + Status = AmlNodeUpdateIntegerValue ( + (AML_OBJECT_NODE*)AmlGetFixedArgument ( + ObjectNode, + EAmlParseIndexTerm0 + ), + IsIncrement, + (UINT64)(*Diff), + &FieldWidthChange + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // FieldWidthChange is an integer. + // It must be positive if IsIncrement is TRUE, negative otherwise. + if ((IsIncrement && + (FieldWidthChange < 0)) || + (!IsIncrement && + (FieldWidthChange > 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Check for UINT32 overflow. + if (*Diff > (MAX_UINT32 - (UINT32)ABS (FieldWidthChange))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update Diff if the field width changed. + *Diff = (UINT32)(*Diff + ABS (FieldWidthChange)); + } // AML_BUFFER_OP node. + + // Update the PgkLen. + // Needs to be done at last to reflect the potential field width changes. + if (AmlNodeHasAttribute (ObjectNode, AML_HAS_PKG_LENGTH)) { + Value = ObjectNode->PkgLen; + + // Subtract the size of the PkgLen encoding. The size of the PkgLen + // encoding must be computed after having updated Value. + InitialPkgLenWidth = AmlComputePkgLengthWidth (Value); + Value -= InitialPkgLenWidth; + + // Check for an over/underflows. + // PkgLen is a 28 bit value, cf 20.2.4 Package Length Encoding + // i.e. the maximum value is (2^28 - 1) = ((BIT0 << 28) - 1). + if ((IsIncrement && ((((BIT0 << 28) - 1) - Value) < *Diff)) || + (!IsIncrement && (Value < *Diff))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update the size. + if (IsIncrement) { + Value += *Diff; + } else { + Value -= *Diff; + } + + // Compute the new PkgLenWidth. + NewPkgLenWidth = AmlComputePkgLengthWidth (Value); + if (NewPkgLenWidth == 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Add it to the Value. + Value += NewPkgLenWidth; + + // Check that adding the PkgLenWidth didn't trigger a domino effect, + // increasing the encoding width of the PkgLen again. + // The PkgLen is encoded on at most 4 bytes. It is possible to increase + // the PkgLen width if its encoding is on less than 3 bytes. + ReComputedPkgLenWidth = AmlComputePkgLengthWidth (Value); + if (ReComputedPkgLenWidth != NewPkgLenWidth) { + if ((ReComputedPkgLenWidth != 0) && + (ReComputedPkgLenWidth < 4)) { + // No need to recompute the PkgLen since a new threshold cannot + // be reached by incrementing the value by one. + Value += 1; + } else { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } + + *Diff += (InitialPkgLenWidth > ReComputedPkgLenWidth) ? + (InitialPkgLenWidth - ReComputedPkgLenWidth) : + (ReComputedPkgLenWidth - InitialPkgLenWidth); + ObjectNode->PkgLen = Value; + } // PkgLen update. + + // During CodeGeneration, the tree is incomplete and + // there is no root node at the top of the tree. Stop + // propagating the new size when finding a root node + // OR when a NULL parent is found. + ParentNode = AmlGetParent ((AML_NODE_HEADER*)Node); + if (ParentNode != NULL) { + // Propagate the size up the tree. + Status = AmlPropagateSize ( + Node->Parent, + IsIncrement, + Diff + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + } else if (IS_AML_ROOT_NODE (Node)) { + // Update the length field in the SDT header. + Value = ((AML_ROOT_NODE*)Node)->SdtHeader->Length; + + // Check for an over/underflows. + if ((IsIncrement && (Value > (MAX_UINT32 - *Diff))) || + (!IsIncrement && (Value < *Diff))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update the size. + if (IsIncrement) { + Value += *Diff; + } else { + Value -= *Diff; + } + + ((AML_ROOT_NODE*)Node)->SdtHeader->Length = Value; + } + + return EFI_SUCCESS; +} + +/** Propagate the node count information up the tree. + + @param [in] ObjectNode Pointer to an object node. + @param [in] IsIncrement Choose the operation to do: + - TRUE: Increment the Node's size and + the Node's count; + - FALSE: Decrement the Node's size and + the Node's count. + @param [in] NodeCount Number of nodes added/removed (depends on the + value of Operation). + @param [out] FieldWidthChange When modifying the integer, it can be + promoted/demoted, e.g. from UINT8 to UINT16. + Stores the change in width. + Can be negative. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlPropagateNodeCount ( + IN AML_OBJECT_NODE * ObjectNode, + IN BOOLEAN IsIncrement, + IN UINT8 NodeCount, + OUT INT8 * FieldWidthChange + ) +{ + EFI_STATUS Status; + + AML_NODE_HEADER * NodeCountArg; + UINT8 CurrNodeCount; + + // Currently there is no use case where (NodeCount > 1). + if (!IS_AML_OBJECT_NODE (ObjectNode) || + (FieldWidthChange == NULL) || + (NodeCount > 1)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *FieldWidthChange = 0; + + // Update the number of elements stored in PackageOp and VarPackageOp. + // The number of elements is stored as the first fixed argument. + // DefPackage := PackageOp PkgLength NumElements PackageElementList + // PackageOp := 0x12 + // DefVarPackage := VarPackageOp PkgLength VarNumElements PackageElementList + // VarPackageOp := 0x13 + // NumElements := ByteData + // VarNumElements := TermArg => Integer + NodeCountArg = AmlGetFixedArgument (ObjectNode, EAmlParseIndexTerm0); + if (AmlNodeCompareOpCode (ObjectNode, AML_PACKAGE_OP, 0)) { + // First fixed argument of PackageOp stores the number of elements + // in the package. It is an UINT8. + + // Check for over/underflow. + CurrNodeCount = *(((AML_DATA_NODE*)NodeCountArg)->Buffer); + if ((IsIncrement && (CurrNodeCount == MAX_UINT8)) || + (!IsIncrement && (CurrNodeCount == 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update the node count in the DataNode. + CurrNodeCount = IsIncrement ? (CurrNodeCount + 1) : (CurrNodeCount - 1); + *(((AML_DATA_NODE*)NodeCountArg)->Buffer) = CurrNodeCount; + } else if (AmlNodeCompareOpCode (ObjectNode, AML_VAR_PACKAGE_OP, 0)) { + // First fixed argument of PackageOp stores the number of elements + // in the package. It is an integer (can be a BYTE, WORD, DWORD, QWORD). + Status = AmlNodeUpdateIntegerValue ( + (AML_OBJECT_NODE*)NodeCountArg, + IsIncrement, + NodeCount, + FieldWidthChange + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + return EFI_SUCCESS; +} + +/** Propagate information up the tree. + + The information can be a new size, a new number of arguments. + + @param [in] Node Pointer to a node. + Must be a root node or an object node. + @param [in] IsIncrement Choose the operation to do: + - TRUE: Increment the Node's size and + the Node's count; + - FALSE: Decrement the Node's size and + the Node's count. + @param [in] Diff Value to add/subtract to the Node's size. + @param [in] NodeCount Number of nodes added/removed. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlPropagateInformation ( + IN AML_NODE_HEADER * Node, + IN BOOLEAN IsIncrement, + IN UINT32 Diff, + IN UINT8 NodeCount + ) +{ + EFI_STATUS Status; + INT8 FieldWidthChange; + + // Currently there is no use case where (NodeCount > 1). + if ((!IS_AML_ROOT_NODE (Node) && + !IS_AML_OBJECT_NODE (Node)) || + (NodeCount > 1)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Propagate the node count first as it may change the number of bytes + // needed to store the node count, and then impact FieldWidthChange. + if ((NodeCount != 0) && + IS_AML_OBJECT_NODE (Node)) { + Status = AmlPropagateNodeCount ( + (AML_OBJECT_NODE*)Node, + IsIncrement, + NodeCount, + &FieldWidthChange + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Propagate the potential field width change. + // Maximum change is between UINT8/UINT64: 8 bytes. + if ((ABS (FieldWidthChange) > 8) || + (IsIncrement && + ((FieldWidthChange < 0) || + ((Diff + (UINT8)FieldWidthChange) > MAX_UINT32))) || + (!IsIncrement && + ((FieldWidthChange > 0) || + (Diff < (UINT32)ABS (FieldWidthChange))))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + Diff = (UINT32)(Diff + (UINT8)ABS (FieldWidthChange)); + } + + // Diff can be zero if some data is updated without modifying the data size. + if (Diff != 0) { + Status = AmlPropagateSize (Node, IsIncrement, &Diff); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + return EFI_SUCCESS; +} diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h new file mode 100644 index 000000000..c57d78014 --- /dev/null +++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h @@ -0,0 +1,95 @@ +/** @file + AML Utility. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_UTILITY_H_ +#define AML_UTILITY_H_ + +#include + +/** This function computes and updates the ACPI table checksum. + + @param [in] AcpiTable Pointer to an Acpi table. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AcpiPlatformChecksum ( + IN EFI_ACPI_DESCRIPTION_HEADER * AcpiTable + ); + +/** Compute the size of a tree/sub-tree. + + @param [in] Node Node to compute the size. + @param [in, out] Size Pointer holding the computed size. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlComputeSize ( + IN CONST AML_NODE_HEADER * Node, + IN OUT UINT32 * Size + ); + +/** Set the value contained in an integer node. + + The OpCode is updated accordingly to the new value + (e.g.: If the original value was a UINT8 value, then the OpCode + would be AML_BYTE_PREFIX. If it the new value is a UINT16 + value then the OpCode will be updated to AML_WORD_PREFIX). + + @param [in] Node Pointer to an integer node. + Must be an object node. + @param [in] NewValue New value to write in the integer node. + @param [out] ValueWidthDiff Difference in number of bytes used to store + the new value. + Can be negative. + + @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 +AmlNodeSetIntegerValue ( + IN AML_OBJECT_NODE * Node, + IN UINT64 NewValue, + OUT INT8 * ValueWidthDiff + ); + +/** Propagate information up the tree. + + The information can be a new size, a new number of arguments. + + @param [in] Node Pointer to a node. + Must be a root node or an object node. + @param [in] IsIncrement Choose the operation to do: + - TRUE: Increment the Node's size and + the Node's count; + - FALSE: Decrement the Node's size and + the Node's count. + @param [in] Diff Value to add/subtract to the Node's size. + @param [in] NodeCount Number of nodes added/removed. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlPropagateInformation ( + IN AML_NODE_HEADER * Node, + IN BOOLEAN IsIncrement, + IN UINT32 Diff, + IN UINT8 NodeCount + ); + +#endif // AML_UTILITY_H_ + -- cgit