summaryrefslogtreecommitdiffstats
path: root/DynamicTablesPkg/Library
diff options
context:
space:
mode:
authorPierre Gondois <pierre.gondois@arm.com>2020-08-04 09:01:18 +0100
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2020-08-13 18:00:06 +0000
commit667aa7ccbf8311a3bbe6ae541cdb6915711a9f3e (patch)
tree593c99ef7fcdfce711c01e7f85e78fc0b477f7d9 /DynamicTablesPkg/Library
parent0414377c021e624dba9d01314464620db3ec3744 (diff)
downloadedk2-667aa7ccbf8311a3bbe6ae541cdb6915711a9f3e.tar.gz
edk2-667aa7ccbf8311a3bbe6ae541cdb6915711a9f3e.tar.bz2
edk2-667aa7ccbf8311a3bbe6ae541cdb6915711a9f3e.zip
DynamicTablesPkg: AML utility interfaces
The AML utility interfaces are a collection of helper functions that assist in computing the checksum, size and to propagate the node information as a result of addition or update of AML nodes. Signed-off-by: Pierre Gondois <pierre.gondois@arm.com> Signed-off-by: Sami Mujawar <sami.mujawar@arm.com> Reviewed-by: Alexei Fedorov <Alexei.Fedorov@arm.com>
Diffstat (limited to 'DynamicTablesPkg/Library')
-rw-r--r--DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c906
-rw-r--r--DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h95
2 files changed, 1001 insertions, 0 deletions
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c b/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c
new file mode 100644
index 0000000000..7ebd08f945
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c
@@ -0,0 +1,906 @@
+/** @file
+ AML Utility.
+
+ Copyright (c) 2019 - 2020, 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.
+**/
+STATIC
+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;
+}
diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h b/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h
new file mode 100644
index 0000000000..c57d780140
--- /dev/null
+++ b/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h
@@ -0,0 +1,95 @@
+/** @file
+ AML Utility.
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_UTILITY_H_
+#define AML_UTILITY_H_
+
+#include <AmlNodeDefines.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
+ );
+
+/** 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
+ );
+
+/** 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
+ );
+
+/** 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
+ );
+
+#endif // AML_UTILITY_H_
+