| /** @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; | |
| } | |
| Status = AmlSetRdListCheckSum (ParentNode, 0); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| return Status; | |
| } | |
| 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; | |
| } |