blob: 811b7d22022853df68345531a2fa48dab408c415 [file] [log] [blame]
/** @file
AML Utility.
Copyright (c) 2019 - 2021, Arm Limited. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Utils/AmlUtility.h>
#include <AmlCoreInterface.h>
#include <Tree/AmlNode.h>
#include <Tree/AmlTree.h>
/** This function computes and updates the ACPI table checksum.
@param [in] AcpiTable Pointer to an Acpi table.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AcpiPlatformChecksum (
IN EFI_ACPI_DESCRIPTION_HEADER *AcpiTable
)
{
UINT8 *Ptr;
UINT8 Sum;
UINT32 Size;
if (AcpiTable == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Ptr = (UINT8 *)AcpiTable;
Size = AcpiTable->Length;
Sum = 0;
// Set the checksum field to 0 first.
AcpiTable->Checksum = 0;
// Compute the checksum.
while ((Size--) != 0) {
Sum = (UINT8)(Sum + (*Ptr++));
}
// Set the checksum.
AcpiTable->Checksum = (UINT8)(0xFF - Sum + 1);
return EFI_SUCCESS;
}
/** A callback function that computes the size of a Node and adds it to the
Size pointer stored in the Context.
Calling this function on the root node will compute the total size of the
AML bytestream.
@param [in] Node Node to compute the size.
@param [in, out] Context Pointer holding the computed size.
(UINT32 *) Context.
@param [in, out] Status Pointer holding:
- At entry, the Status returned by the
last call to this exact function during
the enumeration;
- At exit, he returned status of the
call to this function.
Optional, can be NULL.
@retval TRUE if the enumeration can continue or has finished without
interruption.
@retval FALSE if the enumeration needs to stopped or has stopped.
**/
STATIC
BOOLEAN
EFIAPI
AmlComputeSizeCallback (
IN AML_NODE_HEADER *Node,
IN OUT VOID *Context,
IN OUT EFI_STATUS *Status OPTIONAL
)
{
UINT32 Size;
EAML_PARSE_INDEX IndexPtr;
CONST AML_OBJECT_NODE *ParentNode;
if (!IS_AML_NODE_VALID (Node) ||
(Context == NULL))
{
ASSERT (0);
if (Status != NULL) {
*Status = EFI_INVALID_PARAMETER;
}
return FALSE;
}
// Ignore the second fixed argument of method invocation nodes
// as the information stored there (the argument count) is not in the
// ACPI specification.
ParentNode = (CONST AML_OBJECT_NODE *)AmlGetParent (Node);
if (IS_AML_OBJECT_NODE (ParentNode) &&
AmlNodeCompareOpCode (ParentNode, AML_METHOD_INVOC_OP, 0) &&
AmlIsNodeFixedArgument (Node, &IndexPtr))
{
if (IndexPtr == EAmlParseIndexTerm1) {
if (Status != NULL) {
*Status = EFI_SUCCESS;
}
return TRUE;
}
}
Size = *((UINT32 *)Context);
if (IS_AML_DATA_NODE (Node)) {
Size += ((AML_DATA_NODE *)Node)->Size;
} else if (IS_AML_OBJECT_NODE (Node) &&
!AmlNodeHasAttribute (
(CONST AML_OBJECT_NODE *)Node,
AML_IS_PSEUDO_OPCODE
))
{
// Ignore pseudo-opcodes as they are not part of the
// ACPI specification.
Size += (((AML_OBJECT_NODE *)Node)->AmlByteEncoding->OpCode ==
AML_EXT_OP) ? 2 : 1;
// Add the size of the PkgLen.
if (AmlNodeHasAttribute (
(AML_OBJECT_NODE *)Node,
AML_HAS_PKG_LENGTH
))
{
Size += AmlComputePkgLengthWidth (((AML_OBJECT_NODE *)Node)->PkgLen);
}
}
// Check for overflow.
// The root node has a null size, thus the strict comparison.
if (*((UINT32 *)Context) > Size) {
ASSERT (0);
*Status = EFI_INVALID_PARAMETER;
return FALSE;
}
*((UINT32 *)Context) = Size;
if (Status != NULL) {
*Status = EFI_SUCCESS;
}
return TRUE;
}
/** Compute the size of a tree/sub-tree.
@param [in] Node Node to compute the size.
@param [in, out] Size Pointer holding the computed size.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlComputeSize (
IN CONST AML_NODE_HEADER *Node,
IN OUT UINT32 *Size
)
{
EFI_STATUS Status;
if (!IS_AML_NODE_VALID (Node) ||
(Size == NULL))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
*Size = 0;
AmlEnumTree (
(AML_NODE_HEADER *)Node,
AmlComputeSizeCallback,
(VOID *)Size,
&Status
);
return Status;
}
/** Get the value contained in an integer node.
@param [in] Node Pointer to an integer node.
Must be an object node.
@param [out] Value Value contained in the integer node.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlNodeGetIntegerValue (
IN AML_OBJECT_NODE *Node,
OUT UINT64 *Value
)
{
AML_DATA_NODE *DataNode;
if ((!IsIntegerNode (Node) &&
!IsSpecialIntegerNode (Node)) ||
(Value == NULL))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// For ZeroOp and OneOp, there is no data node.
if (IsSpecialIntegerNode (Node)) {
if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) {
*Value = 0;
} else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) {
*Value = 1;
} else {
// OnesOp cannot be handled: it represents a maximum value.
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
// For integer nodes, the value is in the first fixed argument.
DataNode = (AML_DATA_NODE *)Node->FixedArgs[EAmlParseIndexTerm0];
if (!IS_AML_DATA_NODE (DataNode) ||
(DataNode->DataType != EAmlNodeDataTypeUInt))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
switch (DataNode->Size) {
case 1:
{
*Value = *((UINT8 *)(DataNode->Buffer));
break;
}
case 2:
{
*Value = *((UINT16 *)(DataNode->Buffer));
break;
}
case 4:
{
*Value = *((UINT32 *)(DataNode->Buffer));
break;
}
case 8:
{
*Value = *((UINT64 *)(DataNode->Buffer));
break;
}
default:
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
} // switch
return EFI_SUCCESS;
}
/** Replace a Zero (AML_ZERO_OP) or One (AML_ONE_OP) object node
with a byte integer (AML_BYTE_PREFIX) object node having the same value.
@param [in] Node Pointer to an integer node.
Must be an object node having ZeroOp or OneOp.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
AmlUnwindSpecialInteger (
IN AML_OBJECT_NODE *Node
)
{
EFI_STATUS Status;
AML_DATA_NODE *NewDataNode;
UINT8 Value;
CONST AML_BYTE_ENCODING *ByteEncoding;
if (!IsSpecialIntegerNode (Node)) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Find the value.
if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) {
Value = 0;
} else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) {
Value = 1;
} else {
// OnesOp cannot be handled: it represents a maximum value.
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Status = AmlCreateDataNode (
EAmlNodeDataTypeUInt,
&Value,
sizeof (UINT8),
(AML_DATA_NODE **)&NewDataNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Change the encoding of the special node to a ByteOp encoding.
ByteEncoding = AmlGetByteEncodingByOpCode (AML_BYTE_PREFIX, 0);
if (ByteEncoding == NULL) {
ASSERT (0);
Status = EFI_INVALID_PARAMETER;
goto error_handler;
}
// Update the ByteEncoding from ZERO_OP/ONE_OP to AML_BYTE_PREFIX.
Node->AmlByteEncoding = ByteEncoding;
// Add the data node as the first fixed argument of the ByteOp object.
Status = AmlSetFixedArgument (
(AML_OBJECT_NODE *)Node,
EAmlParseIndexTerm0,
(AML_NODE_HEADER *)NewDataNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler;
}
return Status;
error_handler:
AmlDeleteTree ((AML_NODE_HEADER *)NewDataNode);
return Status;
}
/** Set the value contained in an integer node.
The OpCode is updated accordingly to the new value
(e.g.: If the original value was a UINT8 value, then the OpCode
would be AML_BYTE_PREFIX. If it the new value is a UINT16
value then the OpCode will be updated to AML_WORD_PREFIX).
@param [in] Node Pointer to an integer node.
Must be an object node.
@param [in] NewValue New value to write in the integer node.
@param [out] ValueWidthDiff Difference in number of bytes used to store
the new value.
Can be negative.
@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
AmlNodeSetIntegerValue (
IN AML_OBJECT_NODE *Node,
IN UINT64 NewValue,
OUT INT8 *ValueWidthDiff
)
{
EFI_STATUS Status;
AML_DATA_NODE *DataNode;
UINT8 NewOpCode;
UINT8 NumberOfBytes;
if ((!IsIntegerNode (Node) &&
!IsSpecialIntegerNode (Node)) ||
(ValueWidthDiff == NULL))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
*ValueWidthDiff = 0;
// For ZeroOp and OneOp, there is no data node.
// Thus the object node is converted to a byte object node holding 0 or 1.
if (IsSpecialIntegerNode (Node)) {
switch (NewValue) {
case AML_ZERO_OP:
Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ZERO_OP, 0);
return EFI_SUCCESS;
case AML_ONE_OP:
Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ONE_OP, 0);
return EFI_SUCCESS;
default:
{
Status = AmlUnwindSpecialInteger (Node);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// The AmlUnwindSpecialInteger functions converts a special integer
// node to a UInt8/Byte data node. Thus, the size increments by one:
// special integer are encoded as one byte (the opcode only) while byte
// integers are encoded as two bytes (the opcode + the value).
*ValueWidthDiff += sizeof (UINT8);
}
} // switch
} // IsSpecialIntegerNode (Node)
// For integer nodes, the value is in the first fixed argument.
DataNode = (AML_DATA_NODE *)Node->FixedArgs[EAmlParseIndexTerm0];
if (!IS_AML_DATA_NODE (DataNode) ||
(DataNode->DataType != EAmlNodeDataTypeUInt))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// The value can be encoded with a special 0 or 1 OpCode.
// The AML_ONES_OP is not handled.
if (NewValue <= 1) {
NewOpCode = (NewValue == 0) ? AML_ZERO_OP : AML_ONE_OP;
Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0);
// The value is encoded with a AML_ZERO_OP or AML_ONE_OP.
// This means there is no need for a DataNode containing the value.
// The change in size is equal to the size of the DataNode's buffer.
*ValueWidthDiff = -((INT8)DataNode->Size);
// Detach and free the DataNode containing the integer value.
DataNode->NodeHeader.Parent = NULL;
Node->FixedArgs[EAmlParseIndexTerm0] = NULL;
Status = AmlDeleteNode ((AML_NODE_HEADER *)DataNode);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
return EFI_SUCCESS;
}
// Check the number of bits needed to represent the value.
if (NewValue > MAX_UINT32) {
// Value is 64 bits.
NewOpCode = AML_QWORD_PREFIX;
NumberOfBytes = 8;
} else if (NewValue > MAX_UINT16) {
// Value is 32 bits.
NewOpCode = AML_DWORD_PREFIX;
NumberOfBytes = 4;
} else if (NewValue > MAX_UINT8) {
// Value is 16 bits.
NewOpCode = AML_WORD_PREFIX;
NumberOfBytes = 2;
} else {
// Value is 8 bits.
NewOpCode = AML_BYTE_PREFIX;
NumberOfBytes = 1;
}
*ValueWidthDiff += (INT8)(NumberOfBytes - DataNode->Size);
// Update the ByteEncoding as it may have changed between [8 .. 64] bits.
Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0);
if (Node->AmlByteEncoding == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Free the old DataNode buffer and allocate a buffer with the right size
// to store the new data.
if (*ValueWidthDiff != 0) {
FreePool (DataNode->Buffer);
DataNode->Buffer = AllocateZeroPool (NumberOfBytes);
if (DataNode->Buffer == NULL) {
ASSERT (0);
return EFI_OUT_OF_RESOURCES;
}
DataNode->Size = NumberOfBytes;
}
// Write the new value.
CopyMem (DataNode->Buffer, &NewValue, NumberOfBytes);
return EFI_SUCCESS;
}
/** Increment/decrement the value contained in the IntegerNode.
@param [in] IntegerNode Pointer to an object node containing
an integer.
@param [in] IsIncrement Choose the operation to do:
- TRUE: Increment the Node's size and
the Node's count;
- FALSE: Decrement the Node's size and
the Node's count.
@param [in] Diff Value to add/subtract to the integer.
@param [out] ValueWidthDiff When modifying the integer, it can be
promoted/demoted, e.g. from UINT8 to UINT16.
Stores the change in width.
Can be negative.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
AmlNodeUpdateIntegerValue (
IN AML_OBJECT_NODE *IntegerNode,
IN BOOLEAN IsIncrement,
IN UINT64 Diff,
OUT INT8 *ValueWidthDiff
)
{
EFI_STATUS Status;
UINT64 Value;
if (ValueWidthDiff == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Get the current value.
// Checks on the IntegerNode are done in the call.
Status = AmlNodeGetIntegerValue (IntegerNode, &Value);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Check for UINT64 over/underflow.
if ((IsIncrement && (Value > (MAX_UINT64 - Diff))) ||
(!IsIncrement && (Value < Diff)))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Compute the new value.
if (IsIncrement) {
Value += Diff;
} else {
Value -= Diff;
}
Status = AmlNodeSetIntegerValue (
IntegerNode,
Value,
ValueWidthDiff
);
ASSERT_EFI_ERROR (Status);
return Status;
}
/** Propagate the size information up the tree.
The length of the ACPI table is updated in the RootNode,
but not the checksum.
@param [in] Node Pointer to a node.
Must be a root node or an object node.
@param [in] IsIncrement Choose the operation to do:
- TRUE: Increment the Node's size and
the Node's count;
- FALSE: Decrement the Node's size and
the Node's count.
@param [in] Diff Value to add/subtract to the Node's size.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
AmlPropagateSize (
IN AML_NODE_HEADER *Node,
IN BOOLEAN IsIncrement,
IN UINT32 *Diff
)
{
EFI_STATUS Status;
AML_OBJECT_NODE *ObjectNode;
AML_NODE_HEADER *ParentNode;
UINT32 Value;
UINT32 InitialPkgLenWidth;
UINT32 NewPkgLenWidth;
UINT32 ReComputedPkgLenWidth;
INT8 FieldWidthChange;
if (!IS_AML_OBJECT_NODE (Node) &&
!IS_AML_ROOT_NODE (Node))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
if (IS_AML_OBJECT_NODE (Node)) {
ObjectNode = (AML_OBJECT_NODE *)Node;
// For BufferOp, the buffer size is stored in BufferSize. Therefore,
// BufferOp needs special handling to update the BufferSize.
// BufferSize must be updated before the PkgLen to accommodate any
// increment resulting from the update of the BufferSize.
// DefBuffer := BufferOp PkgLength BufferSize ByteList
// BufferOp := 0x11
// BufferSize := TermArg => Integer
if (AmlNodeCompareOpCode (ObjectNode, AML_BUFFER_OP, 0)) {
// First fixed argument of BufferOp is an integer (BufferSize)
// (can be a BYTE, WORD, DWORD or QWORD).
// BufferSize is an object node.
Status = AmlNodeUpdateIntegerValue (
(AML_OBJECT_NODE *)AmlGetFixedArgument (
ObjectNode,
EAmlParseIndexTerm0
),
IsIncrement,
(UINT64)(*Diff),
&FieldWidthChange
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// FieldWidthChange is an integer.
// It must be positive if IsIncrement is TRUE, negative otherwise.
if ((IsIncrement &&
(FieldWidthChange < 0)) ||
(!IsIncrement &&
(FieldWidthChange > 0)))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Check for UINT32 overflow.
if (*Diff > (MAX_UINT32 - (UINT32)ABS (FieldWidthChange))) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Update Diff if the field width changed.
*Diff = (UINT32)(*Diff + ABS (FieldWidthChange));
} // AML_BUFFER_OP node.
// Update the PgkLen.
// Needs to be done at last to reflect the potential field width changes.
if (AmlNodeHasAttribute (ObjectNode, AML_HAS_PKG_LENGTH)) {
Value = ObjectNode->PkgLen;
// Subtract the size of the PkgLen encoding. The size of the PkgLen
// encoding must be computed after having updated Value.
InitialPkgLenWidth = AmlComputePkgLengthWidth (Value);
Value -= InitialPkgLenWidth;
// Check for an over/underflows.
// PkgLen is a 28 bit value, cf 20.2.4 Package Length Encoding
// i.e. the maximum value is (2^28 - 1) = ((BIT0 << 28) - 1).
if ((IsIncrement && ((((BIT0 << 28) - 1) - Value) < *Diff)) ||
(!IsIncrement && (Value < *Diff)))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Update the size.
if (IsIncrement) {
Value += *Diff;
} else {
Value -= *Diff;
}
// Compute the new PkgLenWidth.
NewPkgLenWidth = AmlComputePkgLengthWidth (Value);
if (NewPkgLenWidth == 0) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Add it to the Value.
Value += NewPkgLenWidth;
// Check that adding the PkgLenWidth didn't trigger a domino effect,
// increasing the encoding width of the PkgLen again.
// The PkgLen is encoded on at most 4 bytes. It is possible to increase
// the PkgLen width if its encoding is on less than 3 bytes.
ReComputedPkgLenWidth = AmlComputePkgLengthWidth (Value);
if (ReComputedPkgLenWidth != NewPkgLenWidth) {
if ((ReComputedPkgLenWidth != 0) &&
(ReComputedPkgLenWidth < 4))
{
// No need to recompute the PkgLen since a new threshold cannot
// be reached by incrementing the value by one.
Value += 1;
} else {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
}
*Diff += (InitialPkgLenWidth > ReComputedPkgLenWidth) ?
(InitialPkgLenWidth - ReComputedPkgLenWidth) :
(ReComputedPkgLenWidth - InitialPkgLenWidth);
ObjectNode->PkgLen = Value;
} // PkgLen update.
// During CodeGeneration, the tree is incomplete and
// there is no root node at the top of the tree. Stop
// propagating the new size when finding a root node
// OR when a NULL parent is found.
ParentNode = AmlGetParent ((AML_NODE_HEADER *)Node);
if (ParentNode != NULL) {
// Propagate the size up the tree.
Status = AmlPropagateSize (
Node->Parent,
IsIncrement,
Diff
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
}
} else if (IS_AML_ROOT_NODE (Node)) {
// Update the length field in the SDT header.
Value = ((AML_ROOT_NODE *)Node)->SdtHeader->Length;
// Check for an over/underflows.
if ((IsIncrement && (Value > (MAX_UINT32 - *Diff))) ||
(!IsIncrement && (Value < *Diff)))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Update the size.
if (IsIncrement) {
Value += *Diff;
} else {
Value -= *Diff;
}
((AML_ROOT_NODE *)Node)->SdtHeader->Length = Value;
}
return EFI_SUCCESS;
}
/** Propagate the node count information up the tree.
@param [in] ObjectNode Pointer to an object node.
@param [in] IsIncrement Choose the operation to do:
- TRUE: Increment the Node's size and
the Node's count;
- FALSE: Decrement the Node's size and
the Node's count.
@param [in] NodeCount Number of nodes added/removed (depends on the
value of Operation).
@param [out] FieldWidthChange When modifying the integer, it can be
promoted/demoted, e.g. from UINT8 to UINT16.
Stores the change in width.
Can be negative.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
AmlPropagateNodeCount (
IN AML_OBJECT_NODE *ObjectNode,
IN BOOLEAN IsIncrement,
IN UINT8 NodeCount,
OUT INT8 *FieldWidthChange
)
{
EFI_STATUS Status;
AML_NODE_HEADER *NodeCountArg;
UINT8 CurrNodeCount;
// Currently there is no use case where (NodeCount > 1).
if (!IS_AML_OBJECT_NODE (ObjectNode) ||
(FieldWidthChange == NULL) ||
(NodeCount > 1))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
*FieldWidthChange = 0;
// Update the number of elements stored in PackageOp and VarPackageOp.
// The number of elements is stored as the first fixed argument.
// DefPackage := PackageOp PkgLength NumElements PackageElementList
// PackageOp := 0x12
// DefVarPackage := VarPackageOp PkgLength VarNumElements PackageElementList
// VarPackageOp := 0x13
// NumElements := ByteData
// VarNumElements := TermArg => Integer
NodeCountArg = AmlGetFixedArgument (ObjectNode, EAmlParseIndexTerm0);
if (AmlNodeCompareOpCode (ObjectNode, AML_PACKAGE_OP, 0)) {
// First fixed argument of PackageOp stores the number of elements
// in the package. It is an UINT8.
// Check for over/underflow.
CurrNodeCount = *(((AML_DATA_NODE *)NodeCountArg)->Buffer);
if ((IsIncrement && (CurrNodeCount == MAX_UINT8)) ||
(!IsIncrement && (CurrNodeCount == 0)))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Update the node count in the DataNode.
CurrNodeCount = IsIncrement ? (CurrNodeCount + 1) : (CurrNodeCount - 1);
*(((AML_DATA_NODE *)NodeCountArg)->Buffer) = CurrNodeCount;
} else if (AmlNodeCompareOpCode (ObjectNode, AML_VAR_PACKAGE_OP, 0)) {
// First fixed argument of PackageOp stores the number of elements
// in the package. It is an integer (can be a BYTE, WORD, DWORD, QWORD).
Status = AmlNodeUpdateIntegerValue (
(AML_OBJECT_NODE *)NodeCountArg,
IsIncrement,
NodeCount,
FieldWidthChange
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
}
return EFI_SUCCESS;
}
/** Propagate information up the tree.
The information can be a new size, a new number of arguments.
@param [in] Node Pointer to a node.
Must be a root node or an object node.
@param [in] IsIncrement Choose the operation to do:
- TRUE: Increment the Node's size and
the Node's count;
- FALSE: Decrement the Node's size and
the Node's count.
@param [in] Diff Value to add/subtract to the Node's size.
@param [in] NodeCount Number of nodes added/removed.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlPropagateInformation (
IN AML_NODE_HEADER *Node,
IN BOOLEAN IsIncrement,
IN UINT32 Diff,
IN UINT8 NodeCount
)
{
EFI_STATUS Status;
INT8 FieldWidthChange;
// Currently there is no use case where (NodeCount > 1).
if ((!IS_AML_ROOT_NODE (Node) &&
!IS_AML_OBJECT_NODE (Node)) ||
(NodeCount > 1))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Propagate the node count first as it may change the number of bytes
// needed to store the node count, and then impact FieldWidthChange.
if ((NodeCount != 0) &&
IS_AML_OBJECT_NODE (Node))
{
Status = AmlPropagateNodeCount (
(AML_OBJECT_NODE *)Node,
IsIncrement,
NodeCount,
&FieldWidthChange
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Propagate the potential field width change.
// Maximum change is between UINT8/UINT64: 8 bytes.
if ((ABS (FieldWidthChange) > 8) ||
(IsIncrement &&
((FieldWidthChange < 0) ||
((Diff + (UINT8)FieldWidthChange) > MAX_UINT32))) ||
(!IsIncrement &&
((FieldWidthChange > 0) ||
(Diff < (UINT32)ABS (FieldWidthChange)))))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Diff = (UINT32)(Diff + (UINT8)ABS (FieldWidthChange));
}
// Diff can be zero if some data is updated without modifying the data size.
if (Diff != 0) {
Status = AmlPropagateSize (Node, IsIncrement, &Diff);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
}
return EFI_SUCCESS;
}
/** Find and set the EndTag's Checksum of a list of Resource Data elements.
Lists of Resource Data elements end with an EndTag (most of the time). This
function finds the EndTag (if present) in a list of Resource Data elements
and sets the checksum.
ACPI 6.4, s6.4.2.9 "End Tag":
"This checksum is generated such that adding it to the sum of all the data
bytes will produce a zero sum."
"If the checksum field is zero, the resource data is treated as if the
checksum operation succeeded. Configuration proceeds normally."
To avoid re-computing checksums, if a new resource data elements is
added/removed/modified in a list of resource data elements, the AmlLib
resets the checksum to 0.
@param [in] BufferOpNode Node having a list of Resource Data elements.
@param [in] CheckSum CheckSum to store in the EndTag.
To ignore/avoid computing the checksum,
give 0.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_NOT_FOUND No EndTag found.
**/
EFI_STATUS
EFIAPI
AmlSetRdListCheckSum (
IN AML_OBJECT_NODE *BufferOpNode,
IN UINT8 CheckSum
)
{
EFI_STATUS Status;
AML_DATA_NODE *LastRdNode;
AML_RD_HEADER RdDataType;
if (!AmlNodeCompareOpCode (BufferOpNode, AML_BUFFER_OP, 0)) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Get the last Resource data node in the variable list of
// argument of the BufferOp node.
LastRdNode = (AML_DATA_NODE *)AmlGetPreviousVariableArgument (
(AML_NODE_HEADER *)BufferOpNode,
NULL
);
if ((LastRdNode == NULL) ||
!IS_AML_DATA_NODE (LastRdNode) ||
(LastRdNode->DataType != EAmlNodeDataTypeResourceData))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Status = AmlGetResourceDataType (LastRdNode, &RdDataType);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Check the LastRdNode is an EndTag.
// It is possible to have only one Resource Data in a BufferOp with
// no EndTag. Return EFI_NOT_FOUND is such case.
if (!AmlRdCompareDescId (
&RdDataType,
AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME)
))
{
ASSERT (0);
return EFI_NOT_FOUND;
}
Status = AmlRdSetEndTagChecksum (LastRdNode->Buffer, CheckSum);
ASSERT_EFI_ERROR (Status);
return Status;
}