summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h183
-rw-r--r--DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c673
-rw-r--r--DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h212
-rw-r--r--DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c566
4 files changed, 1634 insertions, 0 deletions
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h b/DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h
new file mode 100644
index 0000000..fffba6d
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/AmlNodeDefines.h
@@ -0,0 +1,183 @@
+/** @file
+ AML Node Definition.
+
+ Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_NODE_DEFINES_H_
+#define AML_NODE_DEFINES_H_
+
+#include <AmlEncoding/Aml.h>
+#include <IndustryStandard/Acpi.h>
+
+/** 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/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c
new file mode 100644
index 0000000..2c80440
--- /dev/null
+++ b/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;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h
new file mode 100644
index 0000000..3584b57
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h
@@ -0,0 +1,212 @@
+/** @file
+ AML Node.
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_NODE_H_
+#define AML_NODE_H_
+
+#include <AmlNodeDefines.h>
+#include <IndustryStandard/Acpi.h>
+
+/** 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/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c
new file mode 100644
index 0000000..870346c
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c
@@ -0,0 +1,566 @@
+/** @file
+ AML Node Interface.
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <AmlNodeDefines.h>
+
+#include <AmlCoreInterface.h>
+#include <ResourceData/AmlResourceData.h>
+#include <String/AmlString.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTree.h>
+#include <Utils/AmlUtility.h>
+
+/** 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 AsciiCharList>
+ // 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;
+}