/** @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 If success, contains the created AML_ROOT_NODE. Otherwise reset to NULL. @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; } *NewRootNodePtr = NULL; 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 If success, contains the created AML_OBJECT_NODE. Otherwise reset to NULL. @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; } *NewObjectNodePtr = NULL; 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 If success, contains the created AML_DATA_NODE. Otherwise reset to NULL. @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; } *NewDataNodePtr = NULL; 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; }