diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c | 673 |
1 files changed, 673 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c new file mode 100644 index 00000000..00dd77bc --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c @@ -0,0 +1,673 @@ +/** @file + AML Node. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include <Tree/AmlNode.h> + +#include <AmlCoreInterface.h> +#include <Tree/AmlTree.h> + +/** 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; +} |