mirror of https://github.com/acidanthera/audk.git
567 lines
16 KiB
C
567 lines
16 KiB
C
/** @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;
|
|
}
|
|
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;
|
|
}
|