public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Sami Mujawar" <sami.mujawar@arm.com>
To: <devel@edk2.groups.io>
Cc: Sami Mujawar <sami.mujawar@arm.com>, <Alexei.Fedorov@arm.com>,
	<pierre.gondois@arm.com>, <ard.biesheuvel@arm.com>,
	<Matteo.Carlini@arm.com>, <Ben.Adderson@arm.com>, <nd@arm.com>
Subject: [PATCH v1 10/30] DynamicTablesPkg: AML utility interfaces
Date: Wed, 12 Aug 2020 16:22:16 +0100	[thread overview]
Message-ID: <20200812152236.31164-11-sami.mujawar@arm.com> (raw)
In-Reply-To: <20200812152236.31164-1-sami.mujawar@arm.com>

From: Pierre Gondois <pierre.gondois@arm.com>

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>
---
 DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c | 906 ++++++++++++++++++++
 DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h |  95 ++
 2 files changed, 1001 insertions(+)

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c b/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c
new file mode 100644
index 0000000000000000000000000000000000000000..7ebd08f945c0c5ae87a3408152aefa6cc1e78234
--- /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 0000000000000000000000000000000000000000..c57d780140d4f053a225032fb6fc4e7b9991896d
--- /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_
+
-- 
'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'


  parent reply	other threads:[~2020-08-12 15:23 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-08-12 15:22 [PATCH v1 00/30] Add Dynamic AML generation support Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 01/30] DynamicTablesPkg: Introduction to Dynamic AML Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 02/30] DynamicTablesPkg: AmlLib definitions Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 03/30] DynamicTablesPkg: AML grammar definition Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 04/30] DynamicTablesPkg: AML node definitions Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 05/30] DynamicTablesPkg: AML tree interface Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 06/30] DynamicTablesPkg: AML tree enumerator Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 07/30] DynamicTablesPkg: AML tree traversal Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 08/30] DynamicTablesPkg: AML tree iterator Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 09/30] DynamicTablesPkg: AML tree/node cloning Sami Mujawar
2020-08-12 15:22 ` Sami Mujawar [this message]
2020-08-12 15:22 ` [PATCH v1 11/30] DynamicTablesPkg: AML and ASL string helper Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 12/30] DynamicTablesPkg: AML stream interface Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 13/30] DynamicTablesPkg: AML serialise interface Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 14/30] DynamicTablesPkg: AML debug logging Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 15/30] DynamicTablesPkg: AML ACPI Namespace interface Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 16/30] DynamicTablesPkg: AML Parser Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 17/30] DynamicTablesPkg: AML resource data helper Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 18/30] DynamicTablesPkg: AML resource data parser Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 19/30] DynamicTablesPkg: AML Method parser Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 20/30] DynamicTablesPkg: AML Field list parser Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 21/30] DynamicTablesPkg: AML Codegen Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 22/30] DynamicTablesPkg: AML Resource Data Codegen Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 23/30] DynamicTablesPkg: AML Core interface Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 24/30] DynamicTablesPkg: AmlLib APIs Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 25/30] DynamicTablesPkg: Dynamic AML: Add AmlLib library Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 26/30] DynamicTablesPkg: Add AsciiFromHex helper function Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 27/30] DynamicTablesPkg: SSDT Serial Port Fixup library Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 28/30] DynamicTablesPkg: SSDT Serial Port generator Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 29/30] DynamicTablesPkg: Add SSDT Serial port for SPCR Sami Mujawar
2020-08-12 15:22 ` [PATCH v1 30/30] DynamicTablesPkg: Add SSDT Serial port for DBG2 Sami Mujawar
2020-08-13 15:16 ` [edk2-devel] [PATCH v1 00/30] Add Dynamic AML generation support Alexei Fedorov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200812152236.31164-11-sami.mujawar@arm.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox