/** @file
  IORT Table Generator

  Copyright (c) 2017 - 2020, ARM Limited. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent

  @par Reference(s):
  - IO Remapping Table, Platform Design Document,
    Document number: ARM DEN 0049D, Issue D, March 2018

**/

#include <IndustryStandard/IoRemappingTable.h>
#include <Library/AcpiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Protocol/AcpiTable.h>

// Module specific include files.
#include <AcpiTableGenerator.h>
#include <ConfigurationManagerObject.h>
#include <ConfigurationManagerHelper.h>
#include <Library/TableHelperLib.h>
#include <Protocol/ConfigurationManagerProtocol.h>

#include "IortGenerator.h"

/** ARM standard IORT Generator

Requirements:
  The following Configuration Manager Object(s) are required by
  this Generator:
  - EArmObjItsGroup
  - EArmObjNamedComponent
  - EArmObjRootComplex
  - EArmObjSmmuV1SmmuV2
  - EArmObjSmmuV3
  - EArmObjPmcg
  - EArmObjGicItsIdentifierArray
  - EArmObjIdMappingArray
  - EArmObjGicItsIdentifierArray
*/

/** This macro expands to a function that retrieves the ITS
    Group node information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjItsGroup,
  CM_ARM_ITS_GROUP_NODE
  );

/** This macro expands to a function that retrieves the
    Named Component node information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjNamedComponent,
  CM_ARM_NAMED_COMPONENT_NODE
  );

/** This macro expands to a function that retrieves the
     Root Complex node information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjRootComplex,
  CM_ARM_ROOT_COMPLEX_NODE
  );

/** This macro expands to a function that retrieves the
    SMMU v1/v2 node information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjSmmuV1SmmuV2,
  CM_ARM_SMMUV1_SMMUV2_NODE
  );

/** This macro expands to a function that retrieves the
    SMMU v3 node information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjSmmuV3,
  CM_ARM_SMMUV3_NODE
  );

/** This macro expands to a function that retrieves the
    PMCG node information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjPmcg,
  CM_ARM_PMCG_NODE
  );

/** This macro expands to a function that retrieves the
    ITS Identifier Array information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjGicItsIdentifierArray,
  CM_ARM_ITS_IDENTIFIER
  );

/** This macro expands to a function that retrieves the
    Id Mapping Array information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjIdMappingArray,
  CM_ARM_ID_MAPPING
  );

/** This macro expands to a function that retrieves the
    SMMU Interrupt Array information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjSmmuInterruptArray,
  CM_ARM_SMMU_INTERRUPT
  );

/** Returns the size of the ITS Group node.

    @param [in]  Node    Pointer to ITS Group node.

    @retval Size of the ITS Group Node.
**/
STATIC
UINT32
GetItsGroupNodeSize (
  IN  CONST CM_ARM_ITS_GROUP_NODE  *Node
  )
{
  ASSERT (Node != NULL);

  /* Size of ITS Group Node +
     Size of ITS Identifier array
  */
  return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE) +
                  (Node->ItsIdCount * sizeof (UINT32)));
}

/** Returns the total size required for the ITS Group nodes and
    updates the Node Indexer.

    This function calculates the size required for the node group
    and also populates the Node Indexer array with offsets for the
    individual nodes.

    @param [in]       NodeStartOffset Offset from the start of the
                                      IORT where this node group starts.
    @param [in]       NodeList        Pointer to ITS Group node list.
    @param [in]       NodeCount       Count of the ITS Group nodes.
    @param [in, out]  NodeIndexer     Pointer to the next Node Indexer.

    @retval Total size of the ITS Group Nodes.
**/
STATIC
UINT64
GetSizeofItsGroupNodes (
  IN      CONST UINT32                         NodeStartOffset,
  IN      CONST CM_ARM_ITS_GROUP_NODE          *NodeList,
  IN            UINT32                         NodeCount,
  IN OUT        IORT_NODE_INDEXER     **CONST  NodeIndexer
  )
{
  UINT64  Size;

  ASSERT (NodeList != NULL);

  Size = 0;
  while (NodeCount-- != 0) {
    (*NodeIndexer)->Token  = NodeList->Token;
    (*NodeIndexer)->Object = (VOID *)NodeList;
    (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset);
    DEBUG ((
      DEBUG_INFO,
      "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n",
      *NodeIndexer,
      (*NodeIndexer)->Token,
      (*NodeIndexer)->Object,
      (*NodeIndexer)->Offset
      ));

    Size += GetItsGroupNodeSize (NodeList);
    (*NodeIndexer)++;
    NodeList++;
  }

  return Size;
}

/** Returns the size of the Named Component node.

    @param [in]  Node    Pointer to Named Component node.

    @retval Size of the Named Component node.
**/
STATIC
UINT32
GetNamedComponentNodeSize (
  IN  CONST CM_ARM_NAMED_COMPONENT_NODE  *Node
  )
{
  ASSERT (Node != NULL);

  /* Size of Named Component node +
     Size of ID mapping array +
     Size of ASCII string + 'padding to 32-bit word aligned'.
  */
  return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE) +
                  (Node->IdMappingCount *
                   sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE)) +
                  ALIGN_VALUE (AsciiStrSize (Node->ObjectName), 4));
}

/** Returns the total size required for the Named Component nodes and
    updates the Node Indexer.

    This function calculates the size required for the node group
    and also populates the Node Indexer array with offsets for the
    individual nodes.

    @param [in]       NodeStartOffset Offset from the start of the
                                      IORT where this node group starts.
    @param [in]       NodeList        Pointer to Named Component node list.
    @param [in]       NodeCount       Count of the Named Component nodes.
    @param [in, out]  NodeIndexer     Pointer to the next Node Indexer.

    @retval Total size of the Named Component nodes.
**/
STATIC
UINT64
GetSizeofNamedComponentNodes (
  IN      CONST UINT32                              NodeStartOffset,
  IN      CONST CM_ARM_NAMED_COMPONENT_NODE         *NodeList,
  IN            UINT32                              NodeCount,
  IN OUT        IORT_NODE_INDEXER          **CONST  NodeIndexer
  )
{
  UINT64  Size;

  ASSERT (NodeList != NULL);

  Size = 0;
  while (NodeCount-- != 0) {
    (*NodeIndexer)->Token  = NodeList->Token;
    (*NodeIndexer)->Object = (VOID *)NodeList;
    (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset);
    DEBUG ((
      DEBUG_INFO,
      "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n",
      *NodeIndexer,
      (*NodeIndexer)->Token,
      (*NodeIndexer)->Object,
      (*NodeIndexer)->Offset
      ));

    Size += GetNamedComponentNodeSize (NodeList);
    (*NodeIndexer)++;
    NodeList++;
  }

  return Size;
}

/** Returns the size of the Root Complex node.

    @param [in]  Node    Pointer to Root Complex node.

    @retval Size of the Root Complex node.
**/
STATIC
UINT32
GetRootComplexNodeSize (
  IN  CONST CM_ARM_ROOT_COMPLEX_NODE  *Node
  )
{
  ASSERT (Node != NULL);

  /* Size of Root Complex node +
     Size of ID mapping array
  */
  return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_RC_NODE) +
                  (Node->IdMappingCount *
                   sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE)));
}

/** Returns the total size required for the Root Complex nodes and
    updates the Node Indexer.

    This function calculates the size required for the node group
    and also populates the Node Indexer array with offsets for the
    individual nodes.

    @param [in]       NodeStartOffset Offset from the start of the
                                      IORT where this node group starts.
    @param [in]       NodeList        Pointer to Root Complex node list.
    @param [in]       NodeCount       Count of the Root Complex nodes.
    @param [in, out]  NodeIndexer     Pointer to the next Node Indexer.

    @retval Total size of the Root Complex nodes.
**/
STATIC
UINT64
GetSizeofRootComplexNodes (
  IN      CONST UINT32                              NodeStartOffset,
  IN      CONST CM_ARM_ROOT_COMPLEX_NODE            *NodeList,
  IN            UINT32                              NodeCount,
  IN OUT        IORT_NODE_INDEXER          **CONST  NodeIndexer
  )
{
  UINT64  Size;

  ASSERT (NodeList != NULL);

  Size = 0;
  while (NodeCount-- != 0) {
    (*NodeIndexer)->Token  = NodeList->Token;
    (*NodeIndexer)->Object = (VOID *)NodeList;
    (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset);
    DEBUG ((
      DEBUG_INFO,
      "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n",
      *NodeIndexer,
      (*NodeIndexer)->Token,
      (*NodeIndexer)->Object,
      (*NodeIndexer)->Offset
      ));

    Size += GetRootComplexNodeSize (NodeList);
    (*NodeIndexer)++;
    NodeList++;
  }

  return Size;
}

/** Returns the size of the SMMUv1/SMMUv2 node.

    @param [in]  Node    Pointer to SMMUv1/SMMUv2 node list.

    @retval Size of the SMMUv1/SMMUv2 node.
**/
STATIC
UINT32
GetSmmuV1V2NodeSize (
  IN  CONST CM_ARM_SMMUV1_SMMUV2_NODE  *Node
  )
{
  ASSERT (Node != NULL);

  /* Size of SMMU v1/SMMU v2 node +
     Size of ID mapping array +
     Size of context interrupt array +
     Size of PMU interrupt array
  */
  return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE) +
                  (Node->IdMappingCount *
                   sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE)) +
                  (Node->ContextInterruptCount *
                   sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT)) +
                  (Node->PmuInterruptCount *
                   sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT)));
}

/** Returns the total size required for the SMMUv1/SMMUv2 nodes and
    updates the Node Indexer.

    This function calculates the size required for the node group
    and also populates the Node Indexer array with offsets for the
    individual nodes.

    @param [in]       NodeStartOffset Offset from the start of the
                                      IORT where this node group starts.
    @param [in]       NodeList        Pointer to SMMUv1/SMMUv2 node list.
    @param [in]       NodeCount       Count of the SMMUv1/SMMUv2 nodes.
    @param [in, out]  NodeIndexer     Pointer to the next Node Indexer.

    @retval Total size of the SMMUv1/SMMUv2 nodes.
**/
STATIC
UINT64
GetSizeofSmmuV1V2Nodes (
  IN      CONST UINT32                              NodeStartOffset,
  IN      CONST CM_ARM_SMMUV1_SMMUV2_NODE           *NodeList,
  IN            UINT32                              NodeCount,
  IN OUT        IORT_NODE_INDEXER          **CONST  NodeIndexer
  )
{
  UINT64  Size;

  ASSERT (NodeList != NULL);

  Size = 0;
  while (NodeCount-- != 0) {
    (*NodeIndexer)->Token  = NodeList->Token;
    (*NodeIndexer)->Object = (VOID *)NodeList;
    (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset);
    DEBUG ((
      DEBUG_INFO,
      "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n",
      *NodeIndexer,
      (*NodeIndexer)->Token,
      (*NodeIndexer)->Object,
      (*NodeIndexer)->Offset
      ));

    Size += GetSmmuV1V2NodeSize (NodeList);
    (*NodeIndexer)++;
    NodeList++;
  }

  return Size;
}

/** Returns the size of the SMMUv3 node.

    @param [in]  Node    Pointer to SMMUv3 node list.

    @retval Total size of the SMMUv3 nodes.
**/
STATIC
UINT32
GetSmmuV3NodeSize (
  IN  CONST CM_ARM_SMMUV3_NODE  *Node
  )
{
  ASSERT (Node != NULL);

  /* Size of SMMU v1/SMMU v2 node +
     Size of ID mapping array
  */
  return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE) +
                  (Node->IdMappingCount *
                   sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE)));
}

/** Returns the total size required for the SMMUv3 nodes and
    updates the Node Indexer.

    This function calculates the size required for the node group
    and also populates the Node Indexer array with offsets for the
    individual nodes.

    @param [in]       NodeStartOffset Offset from the start of the
                                      IORT where this node group starts.
    @param [in]       NodeList        Pointer to SMMUv3 node list.
    @param [in]       NodeCount       Count of the SMMUv3 nodes.
    @param [in, out]  NodeIndexer     Pointer to the next Node Indexer.

    @retval Total size of the SMMUv3 nodes.
**/
STATIC
UINT64
GetSizeofSmmuV3Nodes (
  IN      CONST UINT32                       NodeStartOffset,
  IN      CONST CM_ARM_SMMUV3_NODE           *NodeList,
  IN            UINT32                       NodeCount,
  IN OUT        IORT_NODE_INDEXER   **CONST  NodeIndexer
  )
{
  UINT64  Size;

  ASSERT (NodeList != NULL);

  Size = 0;
  while (NodeCount-- != 0) {
    (*NodeIndexer)->Token  = NodeList->Token;
    (*NodeIndexer)->Object = (VOID *)NodeList;
    (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset);
    DEBUG ((
      DEBUG_INFO,
      "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n",
      *NodeIndexer,
      (*NodeIndexer)->Token,
      (*NodeIndexer)->Object,
      (*NodeIndexer)->Offset
      ));

    Size += GetSmmuV3NodeSize (NodeList);
    (*NodeIndexer)++;
    NodeList++;
  }

  return Size;
}

/** Returns the size of the PMCG node.

    @param [in]  Node    Pointer to PMCG node.

    @retval Size of the PMCG node.
**/
STATIC
UINT32
GetPmcgNodeSize (
  IN  CONST CM_ARM_PMCG_NODE  *Node
  )
{
  ASSERT (Node != NULL);

  /* Size of PMCG node +
     Size of ID mapping array
  */
  return (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE) +
                  (Node->IdMappingCount *
                   sizeof (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE)));
}

/** Returns the total size required for the PMCG nodes and
    updates the Node Indexer.

    This function calculates the size required for the node group
    and also populates the Node Indexer array with offsets for the
    individual nodes.

    @param [in]       NodeStartOffset Offset from the start of the
                                      IORT where this node group starts.
    @param [in]       NodeList        Pointer to PMCG node list.
    @param [in]       NodeCount       Count of the PMCG nodes.
    @param [in, out]  NodeIndexer     Pointer to the next Node Indexer.

    @retval Total size of the PMCG nodes.
**/
STATIC
UINT64
GetSizeofPmcgNodes (
  IN      CONST UINT32                     NodeStartOffset,
  IN      CONST CM_ARM_PMCG_NODE           *NodeList,
  IN            UINT32                     NodeCount,
  IN OUT        IORT_NODE_INDEXER **CONST  NodeIndexer
  )
{
  UINT64  Size;

  ASSERT (NodeList != NULL);

  Size = 0;
  while (NodeCount-- != 0) {
    (*NodeIndexer)->Token  = NodeList->Token;
    (*NodeIndexer)->Object = (VOID *)NodeList;
    (*NodeIndexer)->Offset = (UINT32)(Size + NodeStartOffset);
    DEBUG ((
      DEBUG_INFO,
      "IORT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n",
      *NodeIndexer,
      (*NodeIndexer)->Token,
      (*NodeIndexer)->Object,
      (*NodeIndexer)->Offset
      ));

    Size += GetPmcgNodeSize (NodeList);
    (*NodeIndexer)++;
    NodeList++;
  }

  return Size;
}

/** Returns the offset of the Node referenced by the Token.

    @param [in]  NodeIndexer  Pointer to node indexer array.
    @param [in]  NodeCount    Count of the nodes.
    @param [in]  Token        Reference token for the node.
    @param [out] NodeOffset   Offset of the node from the
                              start of the IORT table.

    @retval EFI_SUCCESS       Success.
    @retval EFI_NOT_FOUND     No matching token reference
                              found in node indexer array.
**/
STATIC
EFI_STATUS
GetNodeOffsetReferencedByToken (
  IN  IORT_NODE_INDEXER  *NodeIndexer,
  IN  UINT32             NodeCount,
  IN  CM_OBJECT_TOKEN    Token,
  OUT UINT32             *NodeOffset
  )
{
  DEBUG ((
    DEBUG_INFO,
    "IORT: Node Indexer: Search Token = %p\n",
    Token
    ));
  while (NodeCount-- != 0) {
    DEBUG ((
      DEBUG_INFO,
      "IORT: Node Indexer: NodeIndexer->Token = %p, Offset = %d\n",
      NodeIndexer->Token,
      NodeIndexer->Offset
      ));
    if (NodeIndexer->Token == Token) {
      *NodeOffset = NodeIndexer->Offset;
      DEBUG ((
        DEBUG_INFO,
        "IORT: Node Indexer: Token = %p, Found\n",
        Token
        ));
      return EFI_SUCCESS;
    }

    NodeIndexer++;
  }

  DEBUG ((
    DEBUG_INFO,
    "IORT: Node Indexer: Token = %p, Not Found\n",
    Token
    ));
  return EFI_NOT_FOUND;
}

/** Update the Id Mapping Array.

    This function retrieves the Id Mapping Array object referenced by the
    IdMappingToken and updates the IdMapArray.

    @param [in]     This             Pointer to the table Generator.
    @param [in]     CfgMgrProtocol   Pointer to the Configuration Manager
                                     Protocol Interface.
    @param [in]     IdMapArray       Pointer to an array of Id Mappings.
    @param [in]     IdCount          Number of Id Mappings.
    @param [in]     IdMappingToken   Reference Token for retrieving the
                                     Id Mapping Array object.

    @retval EFI_SUCCESS           Table generated successfully.
    @retval EFI_INVALID_PARAMETER A parameter is invalid.
    @retval EFI_NOT_FOUND         The required object was not found.
**/
STATIC
EFI_STATUS
AddIdMappingArray (
  IN      CONST ACPI_TABLE_GENERATOR                   *CONST  This,
  IN      CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL   *CONST  CfgMgrProtocol,
  IN            EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE             *IdMapArray,
  IN            UINT32                                         IdCount,
  IN      CONST CM_OBJECT_TOKEN                                IdMappingToken
  )
{
  EFI_STATUS           Status;
  CM_ARM_ID_MAPPING    *IdMappings;
  UINT32               IdMappingCount;
  ACPI_IORT_GENERATOR  *Generator;

  ASSERT (IdMapArray != NULL);

  Generator = (ACPI_IORT_GENERATOR *)This;

  // Get the Id Mapping Array
  Status = GetEArmObjIdMappingArray (
             CfgMgrProtocol,
             IdMappingToken,
             &IdMappings,
             &IdMappingCount
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Failed to get Id Mapping array. Status = %r\n",
      Status
      ));
    return Status;
  }

  if (IdMappingCount < IdCount) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Failed to get the required number of Id Mappings.\n"
      ));
    return EFI_NOT_FOUND;
  }

  // Populate the Id Mapping array
  while (IdCount-- != 0) {
    Status = GetNodeOffsetReferencedByToken (
               Generator->NodeIndexer,
               Generator->IortNodeCount,
               IdMappings->OutputReferenceToken,
               &IdMapArray->OutputReference
               );
    if (EFI_ERROR (Status)) {
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Failed to get Output Reference for ITS Identifier array."
        "Reference Token = %p"
        " Status = %r\n",
        IdMappings->OutputReferenceToken,
        Status
        ));
      return Status;
    }

    IdMapArray->InputBase  = IdMappings->InputBase;
    IdMapArray->NumIds     = IdMappings->NumIds;
    IdMapArray->OutputBase = IdMappings->OutputBase;
    IdMapArray->Flags      = IdMappings->Flags;

    IdMapArray++;
    IdMappings++;
  } // Id Mapping array

  return EFI_SUCCESS;
}

/** Update the ITS Group Node Information.

    @param [in]     This             Pointer to the table Generator.
    @param [in]     CfgMgrProtocol   Pointer to the Configuration Manager
                                     Protocol Interface.
    @param [in]     Iort             Pointer to IORT table structure.
    @param [in]     NodesStartOffset Offset for the start of the ITS Group
                                     Nodes.
    @param [in]     NodeList         Pointer to an array of ITS Group Node
                                     Objects.
    @param [in]     NodeCount        Number of ITS Group Node Objects.

    @retval EFI_SUCCESS           Table generated successfully.
    @retval EFI_INVALID_PARAMETER A parameter is invalid.
    @retval EFI_NOT_FOUND         The required object was not found.
**/
STATIC
EFI_STATUS
AddItsGroupNodes (
  IN  CONST ACPI_TABLE_GENERATOR                  *CONST  This,
  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  *CONST  CfgMgrProtocol,
  IN  CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE               *Iort,
  IN  CONST UINT32                                        NodesStartOffset,
  IN  CONST CM_ARM_ITS_GROUP_NODE                         *NodeList,
  IN        UINT32                                        NodeCount
  )
{
  EFI_STATUS                          Status;
  EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE  *ItsGroupNode;
  UINT32                              *ItsIds;
  CM_ARM_ITS_IDENTIFIER               *ItsIdentifier;
  UINT32                              ItsIdentifierCount;
  UINT32                              IdIndex;
  UINT64                              NodeLength;

  ASSERT (Iort != NULL);

  ItsGroupNode = (EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE *)((UINT8 *)Iort +
                                                        NodesStartOffset);

  while (NodeCount-- != 0) {
    NodeLength = GetItsGroupNodeSize (NodeList);
    if (NodeLength > MAX_UINT16) {
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: ITS Id Array Node length 0x%lx > MAX_UINT16."
        " Status = %r\n",
        NodeLength,
        Status
        ));
      return Status;
    }

    // Populate the node header
    ItsGroupNode->Node.Type          = EFI_ACPI_IORT_TYPE_ITS_GROUP;
    ItsGroupNode->Node.Length        = (UINT16)NodeLength;
    ItsGroupNode->Node.Revision      = 0;
    ItsGroupNode->Node.Reserved      = EFI_ACPI_RESERVED_DWORD;
    ItsGroupNode->Node.NumIdMappings = 0;
    ItsGroupNode->Node.IdReference   = 0;

    // IORT specific data
    ItsGroupNode->NumItsIdentifiers = NodeList->ItsIdCount;
    ItsIds                          = (UINT32 *)((UINT8 *)ItsGroupNode +
                                                 sizeof (EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE));

    Status = GetEArmObjGicItsIdentifierArray (
               CfgMgrProtocol,
               NodeList->ItsIdToken,
               &ItsIdentifier,
               &ItsIdentifierCount
               );
    if (EFI_ERROR (Status)) {
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Failed to get ITS Identifier array. Status = %r\n",
        Status
        ));
      return Status;
    }

    if (ItsIdentifierCount < ItsGroupNode->NumItsIdentifiers) {
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Failed to get the required number of ITS Identifiers.\n"
        ));
      return EFI_NOT_FOUND;
    }

    // Populate the ITS identifier array
    for (IdIndex = 0; IdIndex < ItsGroupNode->NumItsIdentifiers; IdIndex++) {
      ItsIds[IdIndex] = ItsIdentifier[IdIndex].ItsId;
    } // ITS identifier array

    // Next IORT Group Node
    ItsGroupNode = (EFI_ACPI_6_0_IO_REMAPPING_ITS_NODE *)((UINT8 *)ItsGroupNode +
                                                          ItsGroupNode->Node.Length);
    NodeList++;
  } // IORT Group Node

  return EFI_SUCCESS;
}

/** Update the Named Component Node Information.

    This function updates the Named Component node information in the IORT
    table.

    @param [in]     This             Pointer to the table Generator.
    @param [in]     CfgMgrProtocol   Pointer to the Configuration Manager
                                     Protocol Interface.
    @param [in]     Iort             Pointer to IORT table structure.
    @param [in]     NodesStartOffset Offset for the start of the Named
                                     Component Nodes.
    @param [in]     NodeList         Pointer to an array of Named Component
                                     Node Objects.
    @param [in]     NodeCount        Number of Named Component Node Objects.

    @retval EFI_SUCCESS           Table generated successfully.
    @retval EFI_INVALID_PARAMETER A parameter is invalid.
    @retval EFI_NOT_FOUND         The required object was not found.
**/
STATIC
EFI_STATUS
AddNamedComponentNodes (
  IN      CONST ACPI_TABLE_GENERATOR                   *CONST  This,
  IN      CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL   *CONST  CfgMgrProtocol,
  IN      CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE                *Iort,
  IN      CONST UINT32                                         NodesStartOffset,
  IN      CONST CM_ARM_NAMED_COMPONENT_NODE                    *NodeList,
  IN            UINT32                                         NodeCount
  )
{
  EFI_STATUS                                 Status;
  EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE  *NcNode;
  EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE         *IdMapArray;
  CHAR8                                      *ObjectName;
  UINTN                                      ObjectNameLength;
  UINT64                                     NodeLength;

  ASSERT (Iort != NULL);

  NcNode = (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE *)((UINT8 *)Iort +
                                                         NodesStartOffset);

  while (NodeCount-- != 0) {
    NodeLength = GetNamedComponentNodeSize (NodeList);
    if (NodeLength > MAX_UINT16) {
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Named Component Node length 0x%lx > MAX_UINT16."
        " Status = %r\n",
        NodeLength,
        Status
        ));
      return Status;
    }

    // Populate the node header
    NcNode->Node.Type          = EFI_ACPI_IORT_TYPE_NAMED_COMP;
    NcNode->Node.Length        = (UINT16)NodeLength;
    NcNode->Node.Revision      = 2;
    NcNode->Node.Reserved      = EFI_ACPI_RESERVED_DWORD;
    NcNode->Node.NumIdMappings = NodeList->IdMappingCount;

    ObjectNameLength         = AsciiStrLen (NodeList->ObjectName) + 1;
    NcNode->Node.IdReference =
      (UINT32)(sizeof (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE) +
               (ALIGN_VALUE (ObjectNameLength, 4)));

    // Named Component specific data
    NcNode->Flags             = NodeList->Flags;
    NcNode->CacheCoherent     = NodeList->CacheCoherent;
    NcNode->AllocationHints   = NodeList->AllocationHints;
    NcNode->Reserved          = EFI_ACPI_RESERVED_WORD;
    NcNode->MemoryAccessFlags = NodeList->MemoryAccessFlags;
    NcNode->AddressSizeLimit  = NodeList->AddressSizeLimit;

    // Copy the object name
    ObjectName = (CHAR8 *)((UINT8 *)NcNode +
                           sizeof (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE));
    Status = AsciiStrCpyS (
               ObjectName,
               ObjectNameLength,
               NodeList->ObjectName
               );
    if (EFI_ERROR (Status)) {
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Failed to copy Object Name. Status = %r\n",
        Status
        ));
      return Status;
    }

    if ((NodeList->IdMappingCount > 0) &&
        (NodeList->IdMappingToken != CM_NULL_TOKEN))
    {
      // Ids for Named Component
      IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE *)((UINT8 *)NcNode +
                                                          NcNode->Node.IdReference);

      Status = AddIdMappingArray (
                 This,
                 CfgMgrProtocol,
                 IdMapArray,
                 NodeList->IdMappingCount,
                 NodeList->IdMappingToken
                 );
      if (EFI_ERROR (Status)) {
        DEBUG ((
          DEBUG_ERROR,
          "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n",
          Status
          ));
        return Status;
      }
    }

    // Next Named Component Node
    NcNode = (EFI_ACPI_6_0_IO_REMAPPING_NAMED_COMP_NODE *)((UINT8 *)NcNode +
                                                           NcNode->Node.Length);
    NodeList++;
  } // Named Component Node

  return EFI_SUCCESS;
}

/** Update the Root Complex Node Information.

    This function updates the Root Complex node information in the IORT table.

    @param [in]     This             Pointer to the table Generator.
    @param [in]     CfgMgrProtocol   Pointer to the Configuration Manager
                                     Protocol Interface.
    @param [in]     Iort             Pointer to IORT table structure.
    @param [in]     NodesStartOffset Offset for the start of the Root Complex
                                     Nodes.
    @param [in]     NodeList         Pointer to an array of Root Complex Node
                                     Objects.
    @param [in]     NodeCount        Number of Root Complex Node Objects.

    @retval EFI_SUCCESS           Table generated successfully.
    @retval EFI_INVALID_PARAMETER A parameter is invalid.
    @retval EFI_NOT_FOUND         The required object was not found.
**/
STATIC
EFI_STATUS
AddRootComplexNodes (
  IN      CONST ACPI_TABLE_GENERATOR                   *CONST  This,
  IN      CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL   *CONST  CfgMgrProtocol,
  IN      CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE                *Iort,
  IN      CONST UINT32                                         NodesStartOffset,
  IN      CONST CM_ARM_ROOT_COMPLEX_NODE                       *NodeList,
  IN            UINT32                                         NodeCount
  )
{
  EFI_STATUS                          Status;
  EFI_ACPI_6_0_IO_REMAPPING_RC_NODE   *RcNode;
  EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE  *IdMapArray;
  UINT64                              NodeLength;

  ASSERT (Iort != NULL);

  RcNode = (EFI_ACPI_6_0_IO_REMAPPING_RC_NODE *)((UINT8 *)Iort +
                                                 NodesStartOffset);

  while (NodeCount-- != 0) {
    NodeLength = GetRootComplexNodeSize (NodeList);
    if (NodeLength > MAX_UINT16) {
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Root Complex Node length 0x%lx > MAX_UINT16."
        " Status = %r\n",
        NodeLength,
        Status
        ));
      return Status;
    }

    // Populate the node header
    RcNode->Node.Type          = EFI_ACPI_IORT_TYPE_ROOT_COMPLEX;
    RcNode->Node.Length        = (UINT16)NodeLength;
    RcNode->Node.Revision      = 1;
    RcNode->Node.Reserved      = EFI_ACPI_RESERVED_DWORD;
    RcNode->Node.NumIdMappings = NodeList->IdMappingCount;
    RcNode->Node.IdReference   = sizeof (EFI_ACPI_6_0_IO_REMAPPING_RC_NODE);

    // Root Complex specific data
    RcNode->CacheCoherent     = NodeList->CacheCoherent;
    RcNode->AllocationHints   = NodeList->AllocationHints;
    RcNode->Reserved          = EFI_ACPI_RESERVED_WORD;
    RcNode->MemoryAccessFlags = NodeList->MemoryAccessFlags;
    RcNode->AtsAttribute      = NodeList->AtsAttribute;
    RcNode->PciSegmentNumber  = NodeList->PciSegmentNumber;
    RcNode->MemoryAddressSize = NodeList->MemoryAddressSize;
    RcNode->Reserved1[0]      = EFI_ACPI_RESERVED_BYTE;
    RcNode->Reserved1[1]      = EFI_ACPI_RESERVED_BYTE;
    RcNode->Reserved1[2]      = EFI_ACPI_RESERVED_BYTE;

    if ((NodeList->IdMappingCount > 0) &&
        (NodeList->IdMappingToken != CM_NULL_TOKEN))
    {
      // Ids for Root Complex
      IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE *)((UINT8 *)RcNode +
                                                          RcNode->Node.IdReference);
      Status = AddIdMappingArray (
                 This,
                 CfgMgrProtocol,
                 IdMapArray,
                 NodeList->IdMappingCount,
                 NodeList->IdMappingToken
                 );
      if (EFI_ERROR (Status)) {
        DEBUG ((
          DEBUG_ERROR,
          "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n",
          Status
          ));
        return Status;
      }
    }

    // Next Root Complex Node
    RcNode = (EFI_ACPI_6_0_IO_REMAPPING_RC_NODE *)((UINT8 *)RcNode +
                                                   RcNode->Node.Length);
    NodeList++;
  } // Root Complex Node

  return EFI_SUCCESS;
}

/** Update the SMMU Interrupt Array.

    This function retrieves the InterruptArray object referenced by the
    InterruptToken and updates the SMMU InterruptArray.

    @param [in]      CfgMgrProtocol   Pointer to the Configuration Manager
                                      Protocol Interface.
    @param [in, out] InterruptArray   Pointer to an array of Interrupts.
    @param [in]      InterruptCount   Number of entries in the InterruptArray.
    @param [in]      InterruptToken   Reference Token for retrieving the SMMU
                                      InterruptArray object.

    @retval EFI_SUCCESS           Table generated successfully.
    @retval EFI_INVALID_PARAMETER A parameter is invalid.
    @retval EFI_NOT_FOUND         The required object was not found.
**/
STATIC
EFI_STATUS
AddSmmuInterruptArray (
  IN      CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  *CONST  CfgMgrProtocol,
  IN OUT        EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT            *InterruptArray,
  IN            UINT32                                        InterruptCount,
  IN      CONST CM_OBJECT_TOKEN                               InterruptToken
  )
{
  EFI_STATUS             Status;
  CM_ARM_SMMU_INTERRUPT  *SmmuInterrupt;
  UINT32                 SmmuInterruptCount;

  ASSERT (InterruptArray != NULL);

  // Get the SMMU Interrupt Array
  Status = GetEArmObjSmmuInterruptArray (
             CfgMgrProtocol,
             InterruptToken,
             &SmmuInterrupt,
             &SmmuInterruptCount
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Failed to get SMMU Interrupt array. Status = %r\n",
      Status
      ));
    return Status;
  }

  if (SmmuInterruptCount < InterruptCount) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Failed to get the required number of SMMU Interrupts.\n"
      ));
    return EFI_NOT_FOUND;
  }

  // Populate the Id Mapping array
  while (InterruptCount-- != 0) {
    InterruptArray->Interrupt      = SmmuInterrupt->Interrupt;
    InterruptArray->InterruptFlags = SmmuInterrupt->Flags;
    InterruptArray++;
    SmmuInterrupt++;
  } // Id Mapping array

  return EFI_SUCCESS;
}

/** Update the SMMU v1/v2 Node Information.

    @param [in]     This             Pointer to the table Generator.
    @param [in]     CfgMgrProtocol   Pointer to the Configuration Manager
                                     Protocol Interface.
    @param [in]     Iort             Pointer to IORT table structure.
    @param [in]     NodesStartOffset Offset for the start of the SMMU v1/v2
                                     Nodes.
    @param [in]     NodeList         Pointer to an array of SMMU v1/v2 Node
                                     Objects.
    @param [in]     NodeCount        Number of SMMU v1/v2 Node Objects.

    @retval EFI_SUCCESS           Table generated successfully.
    @retval EFI_INVALID_PARAMETER A parameter is invalid.
    @retval EFI_NOT_FOUND         The required object was not found.
**/
STATIC
EFI_STATUS
AddSmmuV1V2Nodes (
  IN      CONST ACPI_TABLE_GENERATOR                  *CONST  This,
  IN      CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  *CONST  CfgMgrProtocol,
  IN      CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE               *Iort,
  IN      CONST UINT32                                        NodesStartOffset,
  IN      CONST CM_ARM_SMMUV1_SMMUV2_NODE                     *NodeList,
  IN            UINT32                                        NodeCount
  )
{
  EFI_STATUS                           Status;
  EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE  *SmmuNode;
  EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE   *IdMapArray;

  EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT  *ContextInterruptArray;
  EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT  *PmuInterruptArray;
  UINT64                              NodeLength;

  ASSERT (Iort != NULL);

  SmmuNode = (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE *)((UINT8 *)Iort +
                                                     NodesStartOffset);

  while (NodeCount-- != 0) {
    NodeLength = GetSmmuV1V2NodeSize (NodeList);
    if (NodeLength > MAX_UINT16) {
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: SMMU V1/V2 Node length 0x%lx > MAX_UINT16. Status = %r\n",
        NodeLength,
        Status
        ));
      return Status;
    }

    // Populate the node header
    SmmuNode->Node.Type          = EFI_ACPI_IORT_TYPE_SMMUv1v2;
    SmmuNode->Node.Length        = (UINT16)NodeLength;
    SmmuNode->Node.Revision      = 0;
    SmmuNode->Node.Reserved      = EFI_ACPI_RESERVED_DWORD;
    SmmuNode->Node.NumIdMappings = NodeList->IdMappingCount;
    SmmuNode->Node.IdReference   = sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE) +
                                   (NodeList->ContextInterruptCount *
                                    sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT)) +
                                   (NodeList->PmuInterruptCount *
                                    sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT));

    // SMMU v1/v2 specific data
    SmmuNode->Base  = NodeList->BaseAddress;
    SmmuNode->Span  = NodeList->Span;
    SmmuNode->Model = NodeList->Model;
    SmmuNode->Flags = NodeList->Flags;

    // Reference to Global Interrupt Array
    SmmuNode->GlobalInterruptArrayRef =
      OFFSET_OF (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE, SMMU_NSgIrpt);

    // Context Interrupt
    SmmuNode->NumContextInterrupts     = NodeList->ContextInterruptCount;
    SmmuNode->ContextInterruptArrayRef =
      sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE);
    ContextInterruptArray =
      (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT *)((UINT8 *)SmmuNode +
                                             sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE));

    // PMU Interrupt
    SmmuNode->NumPmuInterrupts     = NodeList->PmuInterruptCount;
    SmmuNode->PmuInterruptArrayRef = SmmuNode->ContextInterruptArrayRef +
                                     (NodeList->ContextInterruptCount *
                                      sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT));
    PmuInterruptArray =
      (EFI_ACPI_6_0_IO_REMAPPING_SMMU_INT *)((UINT8 *)SmmuNode +
                                             SmmuNode->PmuInterruptArrayRef);

    SmmuNode->SMMU_NSgIrpt         = NodeList->SMMU_NSgIrpt;
    SmmuNode->SMMU_NSgIrptFlags    = NodeList->SMMU_NSgIrptFlags;
    SmmuNode->SMMU_NSgCfgIrpt      = NodeList->SMMU_NSgCfgIrpt;
    SmmuNode->SMMU_NSgCfgIrptFlags = NodeList->SMMU_NSgCfgIrptFlags;

    // Add Context Interrupt Array
    Status = AddSmmuInterruptArray (
               CfgMgrProtocol,
               ContextInterruptArray,
               SmmuNode->NumContextInterrupts,
               NodeList->ContextInterruptToken
               );
    if (EFI_ERROR (Status)) {
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Failed to Context Interrupt Array. Status = %r\n",
        Status
        ));
      return Status;
    }

    // Add PMU Interrupt Array
    if ((SmmuNode->NumPmuInterrupts > 0) &&
        (NodeList->PmuInterruptToken != CM_NULL_TOKEN))
    {
      Status = AddSmmuInterruptArray (
                 CfgMgrProtocol,
                 PmuInterruptArray,
                 SmmuNode->NumPmuInterrupts,
                 NodeList->PmuInterruptToken
                 );
      if (EFI_ERROR (Status)) {
        DEBUG ((
          DEBUG_ERROR,
          "ERROR: IORT: Failed to PMU Interrupt Array. Status = %r\n",
          Status
          ));
        return Status;
      }
    }

    if ((NodeList->IdMappingCount > 0) &&
        (NodeList->IdMappingToken != CM_NULL_TOKEN))
    {
      // Ids for SMMU v1/v2 Node
      IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE *)((UINT8 *)SmmuNode +
                                                          SmmuNode->Node.IdReference);
      Status = AddIdMappingArray (
                 This,
                 CfgMgrProtocol,
                 IdMapArray,
                 NodeList->IdMappingCount,
                 NodeList->IdMappingToken
                 );
      if (EFI_ERROR (Status)) {
        DEBUG ((
          DEBUG_ERROR,
          "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n",
          Status
          ));
        return Status;
      }
    }

    // Next SMMU v1/v2 Node
    SmmuNode = (EFI_ACPI_6_0_IO_REMAPPING_SMMU_NODE *)((UINT8 *)SmmuNode +
                                                       SmmuNode->Node.Length);
    NodeList++;
  } // SMMU v1/v2 Node

  return EFI_SUCCESS;
}

/** Update the SMMUv3 Node Information.

    This function updates the SMMUv3 node information in the IORT table.

    @param [in]     This             Pointer to the table Generator.
    @param [in]     CfgMgrProtocol   Pointer to the Configuration Manager
                                     Protocol Interface.
    @param [in]     Iort             Pointer to IORT table structure.
    @param [in]     NodesStartOffset Offset for the start of the SMMUv3 Nodes.
    @param [in]     NodeList         Pointer to an array of SMMUv3 Node Objects.
    @param [in]     NodeCount        Number of SMMUv3 Node Objects.

    @retval EFI_SUCCESS           Table generated successfully.
    @retval EFI_INVALID_PARAMETER A parameter is invalid.
    @retval EFI_NOT_FOUND         The required object was not found.
**/
STATIC
EFI_STATUS
AddSmmuV3Nodes (
  IN      CONST ACPI_TABLE_GENERATOR                  *CONST  This,
  IN      CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  *CONST  CfgMgrProtocol,
  IN      CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE               *Iort,
  IN      CONST UINT32                                        NodesStartOffset,
  IN      CONST CM_ARM_SMMUV3_NODE                            *NodeList,
  IN            UINT32                                        NodeCount
  )
{
  EFI_STATUS                            Status;
  EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE  *SmmuV3Node;
  EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE    *IdMapArray;
  UINT64                                NodeLength;

  ASSERT (Iort != NULL);

  SmmuV3Node = (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE *)((UINT8 *)Iort +
                                                        NodesStartOffset);

  while (NodeCount-- != 0) {
    NodeLength = GetSmmuV3NodeSize (NodeList);
    if (NodeLength > MAX_UINT16) {
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: SMMU V3 Node length 0x%lx > MAX_UINT16. Status = %r\n",
        NodeLength,
        Status
        ));
      return Status;
    }

    // Populate the node header
    SmmuV3Node->Node.Type          = EFI_ACPI_IORT_TYPE_SMMUv3;
    SmmuV3Node->Node.Length        = (UINT16)NodeLength;
    SmmuV3Node->Node.Revision      = 2;
    SmmuV3Node->Node.Reserved      = EFI_ACPI_RESERVED_DWORD;
    SmmuV3Node->Node.NumIdMappings = NodeList->IdMappingCount;
    SmmuV3Node->Node.IdReference   =
      sizeof (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE);

    // SMMUv3 specific data
    SmmuV3Node->Base         = NodeList->BaseAddress;
    SmmuV3Node->Flags        = NodeList->Flags;
    SmmuV3Node->Reserved     = EFI_ACPI_RESERVED_WORD;
    SmmuV3Node->VatosAddress = NodeList->VatosAddress;
    SmmuV3Node->Model        = NodeList->Model;
    SmmuV3Node->Event        = NodeList->EventInterrupt;
    SmmuV3Node->Pri          = NodeList->PriInterrupt;
    SmmuV3Node->Gerr         = NodeList->GerrInterrupt;
    SmmuV3Node->Sync         = NodeList->SyncInterrupt;

    if ((SmmuV3Node->Flags & EFI_ACPI_IORT_SMMUv3_FLAG_PROXIMITY_DOMAIN) != 0) {
      // The Proximity Domain Valid flag is set to 1
      SmmuV3Node->ProximityDomain = NodeList->ProximityDomain;
    } else {
      SmmuV3Node->ProximityDomain = 0;
    }

    if ((SmmuV3Node->Event != 0) && (SmmuV3Node->Pri != 0) &&
        (SmmuV3Node->Gerr != 0) && (SmmuV3Node->Sync != 0))
    {
      // If all the SMMU control interrupts are GSIV based,
      // the DeviceID mapping index field is ignored.
      SmmuV3Node->DeviceIdMappingIndex = 0;
    } else {
      SmmuV3Node->DeviceIdMappingIndex = NodeList->DeviceIdMappingIndex;
    }

    if ((NodeList->IdMappingCount > 0) &&
        (NodeList->IdMappingToken != CM_NULL_TOKEN))
    {
      // Ids for SMMUv3 node
      IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE *)((UINT8 *)SmmuV3Node +
                                                          SmmuV3Node->Node.IdReference);
      Status = AddIdMappingArray (
                 This,
                 CfgMgrProtocol,
                 IdMapArray,
                 NodeList->IdMappingCount,
                 NodeList->IdMappingToken
                 );
      if (EFI_ERROR (Status)) {
        DEBUG ((
          DEBUG_ERROR,
          "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n",
          Status
          ));
        return Status;
      }
    }

    // Next SMMUv3 Node
    SmmuV3Node = (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE *)((UINT8 *)SmmuV3Node +
                                                          SmmuV3Node->Node.Length);
    NodeList++;
  } // SMMUv3 Node

  return EFI_SUCCESS;
}

/** Update the PMCG Node Information.

    This function updates the PMCG node information in the IORT table.

    @param [in]     This             Pointer to the table Generator.
    @param [in]     CfgMgrProtocol   Pointer to the Configuration Manager
                                     Protocol Interface.
    @param [in]     Iort             Pointer to IORT table structure.
    @param [in]     NodesStartOffset Offset for the start of the PMCG Nodes.
    @param [in]     NodeList         Pointer to an array of PMCG Node Objects.
    @param [in]     NodeCount        Number of PMCG Node Objects.

    @retval EFI_SUCCESS           Table generated successfully.
    @retval EFI_INVALID_PARAMETER A parameter is invalid.
    @retval EFI_NOT_FOUND         The required object was not found.
**/
STATIC
EFI_STATUS
AddPmcgNodes (
  IN      CONST ACPI_TABLE_GENERATOR                  *CONST  This,
  IN      CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  *CONST  CfgMgrProtocol,
  IN      CONST EFI_ACPI_6_0_IO_REMAPPING_TABLE               *Iort,
  IN      CONST UINT32                                        NodesStartOffset,
  IN      CONST CM_ARM_PMCG_NODE                              *NodeList,
  IN            UINT32                                        NodeCount
  )
{
  EFI_STATUS                           Status;
  EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE  *PmcgNode;
  EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE   *IdMapArray;
  ACPI_IORT_GENERATOR                  *Generator;
  UINT64                               NodeLength;

  ASSERT (Iort != NULL);

  Generator = (ACPI_IORT_GENERATOR *)This;
  PmcgNode  = (EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE *)((UINT8 *)Iort +
                                                      NodesStartOffset);

  while (NodeCount-- != 0) {
    NodeLength = GetPmcgNodeSize (NodeList);
    if (NodeLength > MAX_UINT16) {
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: PMCG Node length 0x%lx > MAX_UINT16. Status = %r\n",
        NodeLength,
        Status
        ));
      return Status;
    }

    // Populate the node header
    PmcgNode->Node.Type          = EFI_ACPI_IORT_TYPE_PMCG;
    PmcgNode->Node.Length        = (UINT16)NodeLength;
    PmcgNode->Node.Revision      = 1;
    PmcgNode->Node.Reserved      = EFI_ACPI_RESERVED_DWORD;
    PmcgNode->Node.NumIdMappings = NodeList->IdMappingCount;
    PmcgNode->Node.IdReference   = sizeof (EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE);

    // PMCG specific data
    PmcgNode->Base                  = NodeList->BaseAddress;
    PmcgNode->OverflowInterruptGsiv = NodeList->OverflowInterrupt;
    PmcgNode->Page1Base             = NodeList->Page1BaseAddress;

    Status = GetNodeOffsetReferencedByToken (
               Generator->NodeIndexer,
               Generator->IortNodeCount,
               NodeList->ReferenceToken,
               &PmcgNode->NodeReference
               );
    if (EFI_ERROR (Status)) {
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Failed to get Output Reference for PMCG Node."
        "Reference Token = %p"
        " Status = %r\n",
        NodeList->ReferenceToken,
        Status
        ));
      return Status;
    }

    if ((NodeList->IdMappingCount > 0) &&
        (NodeList->IdMappingToken != CM_NULL_TOKEN))
    {
      // Ids for PMCG node
      IdMapArray = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE *)((UINT8 *)PmcgNode +
                                                          PmcgNode->Node.IdReference);

      Status = AddIdMappingArray (
                 This,
                 CfgMgrProtocol,
                 IdMapArray,
                 NodeList->IdMappingCount,
                 NodeList->IdMappingToken
                 );
      if (EFI_ERROR (Status)) {
        DEBUG ((
          DEBUG_ERROR,
          "ERROR: IORT: Failed to add Id Mapping Array. Status = %r\n",
          Status
          ));
        return Status;
      }
    }

    // Next PMCG Node
    PmcgNode = (EFI_ACPI_6_0_IO_REMAPPING_PMCG_NODE *)((UINT8 *)PmcgNode +
                                                       PmcgNode->Node.Length);
    NodeList++;
  } // PMCG Node

  return EFI_SUCCESS;
}

/** Construct the IORT ACPI table.

    This function invokes the Configuration Manager protocol interface
    to get the required hardware information for generating the ACPI
    table.

    If this function allocates any resources then they must be freed
    in the FreeXXXXTableResources function.

    @param [in]  This           Pointer to the table generator.
    @param [in]  AcpiTableInfo  Pointer to the ACPI Table Info.
    @param [in]  CfgMgrProtocol Pointer to the Configuration Manager
                                Protocol Interface.
    @param [out] Table          Pointer to the constructed ACPI Table.

    @retval EFI_SUCCESS           Table generated successfully.
    @retval EFI_INVALID_PARAMETER A parameter is invalid.
    @retval EFI_NOT_FOUND         The required object was not found.
    @retval EFI_BAD_BUFFER_SIZE   The size returned by the Configuration
                                  Manager is less than the Object size for the
                                  requested object.
**/
STATIC
EFI_STATUS
EFIAPI
BuildIortTable (
  IN  CONST ACPI_TABLE_GENERATOR                  *CONST  This,
  IN  CONST CM_STD_OBJ_ACPI_TABLE_INFO            *CONST  AcpiTableInfo,
  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  *CONST  CfgMgrProtocol,
  OUT       EFI_ACPI_DESCRIPTION_HEADER          **CONST  Table
  )
{
  EFI_STATUS  Status;

  UINT64  TableSize;
  UINT64  NodeSize;

  UINT32  IortNodeCount;
  UINT32  ItsGroupNodeCount;
  UINT32  NamedComponentNodeCount;
  UINT32  RootComplexNodeCount;
  UINT32  SmmuV1V2NodeCount;
  UINT32  SmmuV3NodeCount;
  UINT32  PmcgNodeCount;

  UINT32  ItsGroupOffset;
  UINT32  NamedComponentOffset;
  UINT32  RootComplexOffset;
  UINT32  SmmuV1V2Offset;
  UINT32  SmmuV3Offset;
  UINT32  PmcgOffset;

  CM_ARM_ITS_GROUP_NODE        *ItsGroupNodeList;
  CM_ARM_NAMED_COMPONENT_NODE  *NamedComponentNodeList;
  CM_ARM_ROOT_COMPLEX_NODE     *RootComplexNodeList;
  CM_ARM_SMMUV1_SMMUV2_NODE    *SmmuV1V2NodeList;
  CM_ARM_SMMUV3_NODE           *SmmuV3NodeList;
  CM_ARM_PMCG_NODE             *PmcgNodeList;

  EFI_ACPI_6_0_IO_REMAPPING_TABLE  *Iort;
  IORT_NODE_INDEXER                *NodeIndexer;
  ACPI_IORT_GENERATOR              *Generator;

  ASSERT (This != NULL);
  ASSERT (AcpiTableInfo != NULL);
  ASSERT (CfgMgrProtocol != NULL);
  ASSERT (Table != NULL);
  ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
  ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);

  if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
      (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision))
  {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Requested table revision = %d, is not supported."
      "Supported table revision: Minimum = %d, Maximum = %d\n",
      AcpiTableInfo->AcpiTableRevision,
      This->MinAcpiTableRevision,
      This->AcpiTableRevision
      ));
    return EFI_INVALID_PARAMETER;
  }

  Generator = (ACPI_IORT_GENERATOR *)This;
  *Table    = NULL;

  // Get the ITS group node info
  Status = GetEArmObjItsGroup (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &ItsGroupNodeList,
             &ItsGroupNodeCount
             );
  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Failed to get ITS Group Node Info. Status = %r\n",
      Status
      ));
    goto error_handler;
  }

  // Add the ITS group node count
  IortNodeCount = ItsGroupNodeCount;

  // Get the Named component node info
  Status = GetEArmObjNamedComponent (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &NamedComponentNodeList,
             &NamedComponentNodeCount
             );
  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Failed to get Named Component Node Info. Status = %r\n",
      Status
      ));
    goto error_handler;
  }

  // Add the Named Component group count
  IortNodeCount += NamedComponentNodeCount;

  // Get the Root complex node info
  Status = GetEArmObjRootComplex (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &RootComplexNodeList,
             &RootComplexNodeCount
             );
  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Failed to get Root Complex Node Info. Status = %r\n",
      Status
      ));
    goto error_handler;
  }

  // Add the Root Complex node count
  IortNodeCount += RootComplexNodeCount;

  // Get the SMMU v1/v2 node info
  Status = GetEArmObjSmmuV1SmmuV2 (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &SmmuV1V2NodeList,
             &SmmuV1V2NodeCount
             );
  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Failed to get SMMUv1/SMMUv2 Node Info. Status = %r\n",
      Status
      ));
    goto error_handler;
  }

  // Add the SMMU v1/v2 node count
  IortNodeCount += SmmuV1V2NodeCount;

  // Get the SMMUv3 node info
  Status = GetEArmObjSmmuV3 (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &SmmuV3NodeList,
             &SmmuV3NodeCount
             );
  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Failed to get SMMUv3 Node Info. Status = %r\n",
      Status
      ));
    goto error_handler;
  }

  // Add the SMMUv3 node count
  IortNodeCount += SmmuV3NodeCount;

  // Get the PMCG node info
  Status = GetEArmObjPmcg (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &PmcgNodeList,
             &PmcgNodeCount
             );
  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Failed to get PMCG Node Info. Status = %r\n",
      Status
      ));
    goto error_handler;
  }

  // Add the PMCG node count
  IortNodeCount += PmcgNodeCount;

  // Allocate Node Indexer array
  NodeIndexer = (IORT_NODE_INDEXER *)AllocateZeroPool (
                                       (sizeof (IORT_NODE_INDEXER) *
                                        IortNodeCount)
                                       );
  if (NodeIndexer == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Failed to allocate memory for Node Indexer" \
      " Status = %r\n",
      Status
      ));
    goto error_handler;
  }

  DEBUG ((DEBUG_INFO, "INFO: NodeIndexer = %p\n", NodeIndexer));
  Generator->IortNodeCount = IortNodeCount;
  Generator->NodeIndexer   = NodeIndexer;

  // Calculate the size of the IORT table
  TableSize = sizeof (EFI_ACPI_6_0_IO_REMAPPING_TABLE);

  // ITS Group Nodes
  if (ItsGroupNodeCount > 0) {
    ItsGroupOffset = (UINT32)TableSize;
    // Size of ITS Group node list.
    NodeSize = GetSizeofItsGroupNodes (
                 ItsGroupOffset,
                 ItsGroupNodeList,
                 ItsGroupNodeCount,
                 &NodeIndexer
                 );
    if (NodeSize > MAX_UINT32) {
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Invalid Size of Group Nodes. Status = %r\n",
        Status
        ));
      goto error_handler;
    }

    TableSize += NodeSize;

    DEBUG ((
      DEBUG_INFO,
      " ItsGroupNodeCount = %d\n" \
      " ItsGroupOffset = %d\n",
      ItsGroupNodeCount,
      ItsGroupOffset
      ));
  }

  // Named Component Nodes
  if (NamedComponentNodeCount > 0) {
    NamedComponentOffset = (UINT32)TableSize;
    // Size of Named Component node list.
    NodeSize = GetSizeofNamedComponentNodes (
                 NamedComponentOffset,
                 NamedComponentNodeList,
                 NamedComponentNodeCount,
                 &NodeIndexer
                 );
    if (NodeSize > MAX_UINT32) {
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Invalid Size of Named Component Nodes. Status = %r\n",
        Status
        ));
      goto error_handler;
    }

    TableSize += NodeSize;

    DEBUG ((
      DEBUG_INFO,
      " NamedComponentNodeCount = %d\n" \
      " NamedComponentOffset = %d\n",
      NamedComponentNodeCount,
      NamedComponentOffset
      ));
  }

  // Root Complex Nodes
  if (RootComplexNodeCount > 0) {
    RootComplexOffset = (UINT32)TableSize;
    // Size of Root Complex node list.
    NodeSize = GetSizeofRootComplexNodes (
                 RootComplexOffset,
                 RootComplexNodeList,
                 RootComplexNodeCount,
                 &NodeIndexer
                 );
    if (NodeSize > MAX_UINT32) {
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Invalid Size of Root Complex Nodes. Status = %r\n",
        Status
        ));
      goto error_handler;
    }

    TableSize += NodeSize;

    DEBUG ((
      DEBUG_INFO,
      " RootComplexNodeCount = %d\n" \
      " RootComplexOffset = %d\n",
      RootComplexNodeCount,
      RootComplexOffset
      ));
  }

  // SMMUv1/SMMUv2 Nodes
  if (SmmuV1V2NodeCount > 0) {
    SmmuV1V2Offset = (UINT32)TableSize;
    // Size of SMMUv1/SMMUv2 node list.
    NodeSize = GetSizeofSmmuV1V2Nodes (
                 SmmuV1V2Offset,
                 SmmuV1V2NodeList,
                 SmmuV1V2NodeCount,
                 &NodeIndexer
                 );
    if (NodeSize > MAX_UINT32) {
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Invalid Size of SMMUv1/v2 Nodes. Status = %r\n",
        Status
        ));
      goto error_handler;
    }

    TableSize += NodeSize;

    DEBUG ((
      DEBUG_INFO,
      " SmmuV1V2NodeCount = %d\n" \
      " SmmuV1V2Offset = %d\n",
      SmmuV1V2NodeCount,
      SmmuV1V2Offset
      ));
  }

  // SMMUv3 Nodes
  if (SmmuV3NodeCount > 0) {
    SmmuV3Offset = (UINT32)TableSize;
    // Size of SMMUv3 node list.
    NodeSize = GetSizeofSmmuV3Nodes (
                 SmmuV3Offset,
                 SmmuV3NodeList,
                 SmmuV3NodeCount,
                 &NodeIndexer
                 );
    if (NodeSize > MAX_UINT32) {
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Invalid Size of SMMUv3 Nodes. Status = %r\n",
        Status
        ));
      goto error_handler;
    }

    TableSize += NodeSize;

    DEBUG ((
      DEBUG_INFO,
      " SmmuV3NodeCount = %d\n" \
      " SmmuV3Offset = %d\n",
      SmmuV3NodeCount,
      SmmuV3Offset
      ));
  }

  // PMCG Nodes
  if (PmcgNodeCount > 0) {
    PmcgOffset = (UINT32)TableSize;
    // Size of PMCG node list.
    NodeSize = GetSizeofPmcgNodes (
                 PmcgOffset,
                 PmcgNodeList,
                 PmcgNodeCount,
                 &NodeIndexer
                 );
    if (NodeSize > MAX_UINT32) {
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Invalid Size of PMCG Nodes. Status = %r\n",
        Status
        ));
      goto error_handler;
    }

    TableSize += NodeSize;

    DEBUG ((
      DEBUG_INFO,
      " PmcgNodeCount = %d\n" \
      " PmcgOffset = %d\n",
      PmcgNodeCount,
      PmcgOffset
      ));
  }

  DEBUG ((
    DEBUG_INFO,
    "INFO: IORT:\n" \
    " IortNodeCount = %d\n" \
    " TableSize = 0x%lx\n",
    IortNodeCount,
    TableSize
    ));

  if (TableSize > MAX_UINT32) {
    Status = EFI_INVALID_PARAMETER;
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: IORT Table Size 0x%lx > MAX_UINT32," \
      " Status = %r\n",
      TableSize,
      Status
      ));
    goto error_handler;
  }

  // Allocate the Buffer for IORT table
  *Table = (EFI_ACPI_DESCRIPTION_HEADER *)AllocateZeroPool (TableSize);
  if (*Table == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Failed to allocate memory for IORT Table, Size = %d," \
      " Status = %r\n",
      TableSize,
      Status
      ));
    goto error_handler;
  }

  Iort = (EFI_ACPI_6_0_IO_REMAPPING_TABLE *)*Table;

  DEBUG ((
    DEBUG_INFO,
    "IORT: Iort = 0x%p TableSize = 0x%lx\n",
    Iort,
    TableSize
    ));

  Status = AddAcpiHeader (
             CfgMgrProtocol,
             This,
             &Iort->Header,
             AcpiTableInfo,
             (UINT32)TableSize
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: IORT: Failed to add ACPI header. Status = %r\n",
      Status
      ));
    goto error_handler;
  }

  // Update IORT table
  Iort->NumNodes   = IortNodeCount;
  Iort->NodeOffset = sizeof (EFI_ACPI_6_0_IO_REMAPPING_TABLE);
  Iort->Reserved   = EFI_ACPI_RESERVED_DWORD;

  if (ItsGroupNodeCount > 0) {
    Status = AddItsGroupNodes (
               This,
               CfgMgrProtocol,
               Iort,
               ItsGroupOffset,
               ItsGroupNodeList,
               ItsGroupNodeCount
               );
    if (EFI_ERROR (Status)) {
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Failed to add ITS Group Node. Status = %r\n",
        Status
        ));
      goto error_handler;
    }
  }

  if (NamedComponentNodeCount > 0) {
    Status = AddNamedComponentNodes (
               This,
               CfgMgrProtocol,
               Iort,
               NamedComponentOffset,
               NamedComponentNodeList,
               NamedComponentNodeCount
               );
    if (EFI_ERROR (Status)) {
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Failed to add Named Component Node. Status = %r\n",
        Status
        ));
      goto error_handler;
    }
  }

  if (RootComplexNodeCount > 0) {
    Status = AddRootComplexNodes (
               This,
               CfgMgrProtocol,
               Iort,
               RootComplexOffset,
               RootComplexNodeList,
               RootComplexNodeCount
               );
    if (EFI_ERROR (Status)) {
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Failed to add Root Complex Node. Status = %r\n",
        Status
        ));
      goto error_handler;
    }
  }

  if (SmmuV1V2NodeCount > 0) {
    Status = AddSmmuV1V2Nodes (
               This,
               CfgMgrProtocol,
               Iort,
               SmmuV1V2Offset,
               SmmuV1V2NodeList,
               SmmuV1V2NodeCount
               );
    if (EFI_ERROR (Status)) {
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Failed to add SMMU v1/v2 Node. Status = %r\n",
        Status
        ));
      goto error_handler;
    }
  }

  if (SmmuV3NodeCount > 0) {
    Status = AddSmmuV3Nodes (
               This,
               CfgMgrProtocol,
               Iort,
               SmmuV3Offset,
               SmmuV3NodeList,
               SmmuV3NodeCount
               );
    if (EFI_ERROR (Status)) {
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Failed to add SMMUv3 Node. Status = %r\n",
        Status
        ));
      goto error_handler;
    }
  }

  if (PmcgNodeCount > 0) {
    Status = AddPmcgNodes (
               This,
               CfgMgrProtocol,
               Iort,
               PmcgOffset,
               PmcgNodeList,
               PmcgNodeCount
               );
    if (EFI_ERROR (Status)) {
      DEBUG ((
        DEBUG_ERROR,
        "ERROR: IORT: Failed to add SMMUv3 Node. Status = %r\n",
        Status
        ));
      goto error_handler;
    }
  }

  return EFI_SUCCESS;

error_handler:
  if (Generator->NodeIndexer != NULL) {
    FreePool (Generator->NodeIndexer);
    Generator->NodeIndexer = NULL;
  }

  if (*Table != NULL) {
    FreePool (*Table);
    *Table = NULL;
  }

  return Status;
}

/** Free any resources allocated for constructing the IORT

  @param [in]      This           Pointer to the table generator.
  @param [in]      AcpiTableInfo  Pointer to the ACPI Table Info.
  @param [in]      CfgMgrProtocol Pointer to the Configuration Manager
                                  Protocol Interface.
  @param [in, out] Table          Pointer to the ACPI Table.

  @retval EFI_SUCCESS           The resources were freed successfully.
  @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.
**/
STATIC
EFI_STATUS
FreeIortTableResources (
  IN      CONST ACPI_TABLE_GENERATOR                  *CONST  This,
  IN      CONST CM_STD_OBJ_ACPI_TABLE_INFO            *CONST  AcpiTableInfo,
  IN      CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  *CONST  CfgMgrProtocol,
  IN OUT        EFI_ACPI_DESCRIPTION_HEADER          **CONST  Table
  )
{
  ACPI_IORT_GENERATOR  *Generator;

  ASSERT (This != NULL);
  ASSERT (AcpiTableInfo != NULL);
  ASSERT (CfgMgrProtocol != NULL);
  ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
  ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);

  Generator = (ACPI_IORT_GENERATOR *)This;

  // Free any memory allocated by the generator
  if (Generator->NodeIndexer != NULL) {
    FreePool (Generator->NodeIndexer);
    Generator->NodeIndexer = NULL;
  }

  if ((Table == NULL) || (*Table == NULL)) {
    DEBUG ((DEBUG_ERROR, "ERROR: IORT: Invalid Table Pointer\n"));
    ASSERT ((Table != NULL) && (*Table != NULL));
    return EFI_INVALID_PARAMETER;
  }

  FreePool (*Table);
  *Table = NULL;
  return EFI_SUCCESS;
}

/** The IORT Table Generator revision.
*/
#define IORT_GENERATOR_REVISION  CREATE_REVISION (1, 0)

/** The interface for the MADT Table Generator.
*/
STATIC
ACPI_IORT_GENERATOR  IortGenerator = {
  // ACPI table generator header
  {
    // Generator ID
    CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdIort),
    // Generator Description
    L"ACPI.STD.IORT.GENERATOR",
    // ACPI Table Signature
    EFI_ACPI_6_2_IO_REMAPPING_TABLE_SIGNATURE,
    // ACPI Table Revision supported by this Generator
    EFI_ACPI_IO_REMAPPING_TABLE_REVISION,
    // Minimum supported ACPI Table Revision
    EFI_ACPI_IO_REMAPPING_TABLE_REVISION,
    // Creator ID
    TABLE_GENERATOR_CREATOR_ID_ARM,
    // Creator Revision
    IORT_GENERATOR_REVISION,
    // Build Table function
    BuildIortTable,
    // Free Resource function
    FreeIortTableResources,
    // Extended build function not needed
    NULL,
    // Extended build function not implemented by the generator.
    // Hence extended free resource function is not required.
    NULL
  },

  // IORT Generator private data

  // Iort Node count
  0,
  // Pointer to Iort node indexer
  NULL
};

/** Register the Generator with the ACPI Table Factory.

    @param [in]  ImageHandle  The handle to the image.
    @param [in]  SystemTable  Pointer to the System Table.

    @retval EFI_SUCCESS           The Generator is registered.
    @retval EFI_INVALID_PARAMETER A parameter is invalid.
    @retval EFI_ALREADY_STARTED   The Generator for the Table ID
                                  is already registered.
**/
EFI_STATUS
EFIAPI
AcpiIortLibConstructor (
  IN  EFI_HANDLE        ImageHandle,
  IN  EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  Status = RegisterAcpiTableGenerator (&IortGenerator.Header);
  DEBUG ((DEBUG_INFO, "IORT: Register Generator. Status = %r\n", Status));
  ASSERT_EFI_ERROR (Status);
  return Status;
}

/** Deregister the Generator from the ACPI Table Factory.

    @param [in]  ImageHandle  The handle to the image.
    @param [in]  SystemTable  Pointer to the System Table.

    @retval EFI_SUCCESS           The Generator is deregistered.
    @retval EFI_INVALID_PARAMETER A parameter is invalid.
    @retval EFI_NOT_FOUND         The Generator is not registered.
**/
EFI_STATUS
EFIAPI
AcpiIortLibDestructor (
  IN  EFI_HANDLE        ImageHandle,
  IN  EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  Status = DeregisterAcpiTableGenerator (&IortGenerator.Header);
  DEBUG ((DEBUG_INFO, "Iort: Deregister Generator. Status = %r\n", Status));
  ASSERT_EFI_ERROR (Status);
  return Status;
}
