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