/** @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; | |
} |