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