/** @file
AML Node Interface.
Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
/** 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 := 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;
}