/** @file
  PCCT Table Generator

  Copyright (c) 2022, Arm Limited. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

  @par Reference(s):
  - ACPI 6.4 Specification - January 2021
    s14 PLATFORM COMMUNICATIONS CHANNEL (PCC)

**/

#include <Library/AcpiLib.h>
#include <Library/BaseMemoryLib.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 "PcctGenerator.h"

/** ARM standard PCCT Generator

Requirements:
  The following Configuration Manager Object(s) are required by
  this Generator:
  - EArmObjPccSubspaceType0Info
  - EArmObjPccSubspaceType1Info
  - EArmObjPccSubspaceType2Info
  - EArmObjPccSubspaceType3Info
  - EArmObjPccSubspaceType4Info
  - EArmObjPccSubspaceType5Info
*/

/** This macro expands to a function that retrieves the PCC
    Subspace of Type 0 Information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjPccSubspaceType0Info,
  CM_ARM_PCC_SUBSPACE_TYPE0_INFO
  );

/** This macro expands to a function that retrieves the PCC
    Subspace of Type 1 Information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjPccSubspaceType1Info,
  CM_ARM_PCC_SUBSPACE_TYPE1_INFO
  );

/** This macro expands to a function that retrieves the PCC
    Subspace of Type 2 Information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjPccSubspaceType2Info,
  CM_ARM_PCC_SUBSPACE_TYPE2_INFO
  );

/** This macro expands to a function that retrieves the PCC
    Subspace of Type 3 Information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjPccSubspaceType3Info,
  CM_ARM_PCC_SUBSPACE_TYPE3_INFO
  );

/** This macro expands to a function that retrieves the PCC
    Subspace of Type 4 Information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjPccSubspaceType4Info,
  CM_ARM_PCC_SUBSPACE_TYPE4_INFO
  );

/** This macro expands to a function that retrieves the PCC
    Subspace of Type 5 Information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceArm,
  EArmObjPccSubspaceType5Info,
  CM_ARM_PCC_SUBSPACE_TYPE5_INFO
  );

/** The Platform is capable of generating an interrupt
    to indicate completion of a command.

  Cf: s14.1.1 Platform Communications Channel Global Flags
  Platform Interrupt flag
  and s14.1.6 Extended PCC subspaces (types 3 and 4)
    If a responder subspace is included in the PCCT,
    then the global Platform Interrupt flag must be set to 1

  Set this variable and populate the PCCT flag accordingly if either:
   - One of the PCCT Subspace uses interrupts.
   - A PCC Subspace of type 4 is used.
*/
STATIC BOOLEAN  mHasPlatformInterrupt;

/** Initialize the MappingTable.

  @param [in] MappingTable  The mapping table structure.
  @param [in] Count         Number of entries to allocate in the
                            MappingTable.

  @retval EFI_SUCCESS            Success.
  @retval EFI_INVALID_PARAMETER  Invalid parameter.
  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
**/
STATIC
EFI_STATUS
EFIAPI
MappingTableInitialize (
  IN  MAPPING_TABLE  *MappingTable,
  IN  UINT32         Count
  )
{
  VOID  **Table;

  if ((MappingTable == NULL)  ||
      (Count == 0))
  {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  Table = AllocateZeroPool (sizeof (*Table) * Count);
  if (Table == NULL) {
    ASSERT (0);
    return EFI_OUT_OF_RESOURCES;
  }

  MappingTable->Table    = Table;
  MappingTable->MaxIndex = Count;

  return EFI_SUCCESS;
}

/** Free the MappingTable.

  @param [in, out]  MappingTable  The mapping table structure.
**/
STATIC
VOID
EFIAPI
MappingTableFree (
  IN  OUT MAPPING_TABLE  *MappingTable
  )
{
  ASSERT (MappingTable != NULL);
  ASSERT (MappingTable->Table != NULL);

  if (MappingTable->Table != NULL) {
    FreePool (MappingTable->Table);
  }
}

/** Add a new entry for CmArmPccSubspace at Index.

  @param [in] MappingTable      The mapping table structure.
  @param [in] CmArmPccSubspace  Pointer to a CM_ARM_PCC_SUBSPACE_TYPE[X]_INFO.
  @param [in] Index             Index at which CmArmPccSubspace must be added.
                                This is the Subspace Id.

  @retval EFI_SUCCESS            Success.
  @retval EFI_BUFFER_TOO_SMALL   Buffer too small.
  @retval EFI_INVALID_PARAMETER  Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
MappingTableAdd (
  IN  MAPPING_TABLE  *MappingTable,
  IN  VOID           *CmArmPccSubspace,
  IN  UINT32         Index
  )
{
  if ((MappingTable == NULL)        ||
      (MappingTable->Table == NULL) ||
      (CmArmPccSubspace == NULL))
  {
    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
    return EFI_INVALID_PARAMETER;
  }

  if ((Index >= MappingTable->MaxIndex) ||
      (MappingTable->Table[Index] != 0))
  {
    ASSERT_EFI_ERROR (EFI_BUFFER_TOO_SMALL);
    return EFI_BUFFER_TOO_SMALL;
  }

  // Just map the Pcc Subspace in the Table.
  MappingTable->Table[Index] = CmArmPccSubspace;
  return EFI_SUCCESS;
}

/** Parse the CmPccArray objects and add them to the MappingTable.

  @param [in] MappingTable     The mapping table structure.
  @param [in] CmPccArray       Pointer to an array of CM_ARM_PCC_SUBSPACE_TYPE[X]_INFO.
  @param [in] CmPccCount       Count of objects in CmPccArray.

  @retval EFI_SUCCESS            Success.
  @retval EFI_BUFFER_TOO_SMALL   Buffer too small.
  @retval EFI_INVALID_PARAMETER  Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
MapPccSubspaceId (
  IN  MAPPING_TABLE  *MappingTable,
  IN  VOID           *CmPccArray,
  IN  UINT32         CmPccCount
  )
{
  EFI_STATUS                 Status;
  UINT8                      *PccBuffer;
  UINT32                     Index;
  UINT32                     CmObjSize;
  PCC_SUBSPACE_GENERIC_INFO  *GenericPcc;

  if (CmPccCount == 0) {
    return EFI_SUCCESS;
  }

  if ((CmPccArray == NULL) || (MappingTable == NULL)) {
    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
    return EFI_INVALID_PARAMETER;
  }

  GenericPcc = (PCC_SUBSPACE_GENERIC_INFO *)CmPccArray;

  switch (GenericPcc->Type) {
    case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_GENERIC:
      CmObjSize = sizeof (CM_ARM_PCC_SUBSPACE_TYPE0_INFO);
      break;

    case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_1_HW_REDUCED_COMMUNICATIONS:
      CmObjSize = sizeof (CM_ARM_PCC_SUBSPACE_TYPE1_INFO);
      break;

    case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_2_HW_REDUCED_COMMUNICATIONS:
      CmObjSize = sizeof (CM_ARM_PCC_SUBSPACE_TYPE2_INFO);
      break;

    case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_3_EXTENDED_PCC:
      CmObjSize = sizeof (CM_ARM_PCC_SUBSPACE_TYPE3_INFO);
      break;

    case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_4_EXTENDED_PCC:
      CmObjSize = sizeof (CM_ARM_PCC_SUBSPACE_TYPE4_INFO);
      break;

    case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_5_HW_REGISTERS_COMMUNICATIONS:
      CmObjSize = sizeof (CM_ARM_PCC_SUBSPACE_TYPE5_INFO);
      break;

    default:
      ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
      return EFI_INVALID_PARAMETER;
  }

  PccBuffer = (UINT8 *)CmPccArray;

  // Map the Pcc channel to their Subspace Id.
  for (Index = 0; Index < CmPccCount; Index++) {
    GenericPcc = (PCC_SUBSPACE_GENERIC_INFO *)PccBuffer;

    Status = MappingTableAdd (
               MappingTable,
               PccBuffer,
               GenericPcc->SubspaceId
               );
    if (EFI_ERROR (Status)) {
      ASSERT_EFI_ERROR (Status);
      return Status;
    }

    PccBuffer += CmObjSize;
  }

  return EFI_SUCCESS;
}

/** Add one PCCT Subspace structure of Type 0 (Generic).

  @param [in]  PccCmObj   Pointer to a CmObj PCCT Subspace info structure.
  @param [in]  PccAcpi    Pointer to the ACPI PCCT Subspace structure to populate.

  @retval EFI_SUCCESS           Table generated successfully.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
**/
STATIC
EFI_STATUS
EFIAPI
AddSubspaceStructType0 (
  IN  CM_ARM_PCC_SUBSPACE_TYPE0_INFO      *PccCmObj,
  IN  EFI_ACPI_6_4_PCCT_SUBSPACE_GENERIC  *PccAcpi
  )
{
  PCC_MAILBOX_REGISTER_INFO         *Doorbell;
  PCC_SUBSPACE_CHANNEL_TIMING_INFO  *ChannelTiming;

  if ((PccCmObj == NULL) ||
      (PccCmObj->Type != EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_GENERIC)  ||
      (PccAcpi == NULL))
  {
    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
    return EFI_INVALID_PARAMETER;
  }

  Doorbell      = &PccCmObj->DoorbellReg;
  ChannelTiming = &PccCmObj->ChannelTiming;

  PccAcpi->Type                    = PccCmObj->Type;
  PccAcpi->Length                  = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_GENERIC);
  *(UINT32 *)&PccAcpi->Reserved[0] = EFI_ACPI_RESERVED_DWORD;
  *(UINT16 *)&PccAcpi->Reserved[4] = EFI_ACPI_RESERVED_WORD;
  PccAcpi->BaseAddress             = PccCmObj->BaseAddress;
  PccAcpi->AddressLength           = PccCmObj->AddressLength;

  CopyMem (
    &PccAcpi->DoorbellRegister,
    &Doorbell->Register,
    sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
    );
  PccAcpi->DoorbellPreserve = Doorbell->PreserveMask;
  PccAcpi->DoorbellWrite    = Doorbell->WriteMask;

  PccAcpi->NominalLatency               = ChannelTiming->NominalLatency;
  PccAcpi->MaximumPeriodicAccessRate    = ChannelTiming->MaxPeriodicAccessRate;
  PccAcpi->MinimumRequestTurnaroundTime = ChannelTiming->MinRequestTurnaroundTime;

  return EFI_SUCCESS;
}

/** Add one PCCT subspace structure of Type 1 (HW-Reduced).

  @param [in]  PccCmObj   Pointer to a CmObj PCCT Subspace info structure.
  @param [in]  PccAcpi    Pointer to the ACPI PCCT Subspace structure to populate.

  @retval EFI_SUCCESS           Table generated successfully.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
**/
STATIC
EFI_STATUS
EFIAPI
AddSubspaceStructType1 (
  IN  CM_ARM_PCC_SUBSPACE_TYPE1_INFO                          *PccCmObj,
  IN  EFI_ACPI_6_4_PCCT_SUBSPACE_1_HW_REDUCED_COMMUNICATIONS  *PccAcpi
  )
{
  CM_ARM_PCC_SUBSPACE_TYPE0_INFO    *GenericPccCmObj;
  PCC_MAILBOX_REGISTER_INFO         *Doorbell;
  PCC_SUBSPACE_CHANNEL_TIMING_INFO  *ChannelTiming;

  GenericPccCmObj = (CM_ARM_PCC_SUBSPACE_TYPE0_INFO *)PccCmObj;

  if ((PccCmObj == NULL) ||
      (GenericPccCmObj->Type != EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_1_HW_REDUCED_COMMUNICATIONS)  ||
      (PccAcpi == NULL))
  {
    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
    return EFI_INVALID_PARAMETER;
  }

  Doorbell      = &GenericPccCmObj->DoorbellReg;
  ChannelTiming = &GenericPccCmObj->ChannelTiming;

  PccAcpi->Type                   = GenericPccCmObj->Type;
  PccAcpi->Length                 = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_1_HW_REDUCED_COMMUNICATIONS);
  PccAcpi->PlatformInterrupt      = PccCmObj->PlatIrq.Interrupt;
  PccAcpi->PlatformInterruptFlags = PccCmObj->PlatIrq.Flags;
  PccAcpi->Reserved               = EFI_ACPI_RESERVED_BYTE;
  PccAcpi->BaseAddress            = GenericPccCmObj->BaseAddress;
  PccAcpi->AddressLength          = GenericPccCmObj->AddressLength;

  CopyMem (
    &PccAcpi->DoorbellRegister,
    &Doorbell->Register,
    sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
    );
  PccAcpi->DoorbellPreserve = Doorbell->PreserveMask;
  PccAcpi->DoorbellWrite    = Doorbell->WriteMask;

  PccAcpi->NominalLatency               = ChannelTiming->NominalLatency;
  PccAcpi->MaximumPeriodicAccessRate    = ChannelTiming->MaxPeriodicAccessRate;
  PccAcpi->MinimumRequestTurnaroundTime = ChannelTiming->MinRequestTurnaroundTime;

  if ((PccCmObj->PlatIrq.Interrupt != 0)) {
    mHasPlatformInterrupt = TRUE;
  }

  return EFI_SUCCESS;
}

/** Add one PCCT subspace structure of Type 2 (HW-Reduced).

  @param [in]  PccCmObj   Pointer to a CmObj PCCT Subspace info structure.
  @param [in]  PccAcpi    Pointer to the ACPI PCCT Subspace structure to populate.

  @retval EFI_SUCCESS           Table generated successfully.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
**/
STATIC
EFI_STATUS
EFIAPI
AddSubspaceStructType2 (
  IN  CM_ARM_PCC_SUBSPACE_TYPE2_INFO                          *PccCmObj,
  IN  EFI_ACPI_6_4_PCCT_SUBSPACE_2_HW_REDUCED_COMMUNICATIONS  *PccAcpi
  )
{
  CM_ARM_PCC_SUBSPACE_TYPE0_INFO    *GenericPccCmObj;
  PCC_MAILBOX_REGISTER_INFO         *Doorbell;
  PCC_MAILBOX_REGISTER_INFO         *PlatIrqAck;
  PCC_SUBSPACE_CHANNEL_TIMING_INFO  *ChannelTiming;

  GenericPccCmObj = (CM_ARM_PCC_SUBSPACE_TYPE0_INFO *)PccCmObj;

  if ((PccCmObj == NULL) ||
      (GenericPccCmObj->Type != EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_2_HW_REDUCED_COMMUNICATIONS)  ||
      (PccAcpi == NULL))
  {
    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
    return EFI_INVALID_PARAMETER;
  }

  Doorbell      = &GenericPccCmObj->DoorbellReg;
  PlatIrqAck    = &PccCmObj->PlatIrqAckReg;
  ChannelTiming = &GenericPccCmObj->ChannelTiming;

  PccAcpi->Type                   = GenericPccCmObj->Type;
  PccAcpi->Length                 = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_2_HW_REDUCED_COMMUNICATIONS);
  PccAcpi->PlatformInterrupt      = PccCmObj->PlatIrq.Interrupt;
  PccAcpi->PlatformInterruptFlags = PccCmObj->PlatIrq.Flags;
  PccAcpi->BaseAddress            = GenericPccCmObj->BaseAddress;
  PccAcpi->Reserved               = EFI_ACPI_RESERVED_BYTE;
  PccAcpi->BaseAddress            = GenericPccCmObj->BaseAddress;
  PccAcpi->AddressLength          = GenericPccCmObj->AddressLength;

  CopyMem (
    &PccAcpi->DoorbellRegister,
    &Doorbell->Register,
    sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
    );
  PccAcpi->DoorbellPreserve = Doorbell->PreserveMask;
  PccAcpi->DoorbellWrite    = Doorbell->WriteMask;

  PccAcpi->NominalLatency               = ChannelTiming->NominalLatency;
  PccAcpi->MaximumPeriodicAccessRate    = ChannelTiming->MaxPeriodicAccessRate;
  PccAcpi->MinimumRequestTurnaroundTime = ChannelTiming->MinRequestTurnaroundTime;

  CopyMem (
    &PccAcpi->PlatformInterruptAckRegister,
    &PlatIrqAck->Register,
    sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
    );
  PccAcpi->PlatformInterruptAckPreserve = PlatIrqAck->PreserveMask;
  PccAcpi->PlatformInterruptAckWrite    = PlatIrqAck->WriteMask;

  if ((PccCmObj->PlatIrq.Interrupt != 0)) {
    mHasPlatformInterrupt = TRUE;
  }

  return EFI_SUCCESS;
}

/** Add one PCCT subspace structure of Type 3 or 4 (Extended).

  @param [in]  PccCmObj   Pointer to a CmObj PCCT Subspace info structure.
  @param [in]  PccAcpi    Pointer to the ACPI PCCT Subspace structure to populate.

  @retval EFI_SUCCESS           Table generated successfully.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
**/
STATIC
EFI_STATUS
EFIAPI
AddSubspaceStructType34 (
  IN  CM_ARM_PCC_SUBSPACE_TYPE3_INFO             *PccCmObj,
  IN  EFI_ACPI_6_4_PCCT_SUBSPACE_3_EXTENDED_PCC  *PccAcpi
  )
{
  CM_ARM_PCC_SUBSPACE_TYPE0_INFO    *GenericPccCmObj;
  PCC_MAILBOX_REGISTER_INFO         *Doorbell;
  PCC_MAILBOX_REGISTER_INFO         *PlatIrqAck;
  PCC_MAILBOX_REGISTER_INFO         *CmdCompleteCheck;
  PCC_MAILBOX_REGISTER_INFO         *CmdCompleteUpdate;
  PCC_MAILBOX_REGISTER_INFO         *ErrorStatus;
  PCC_SUBSPACE_CHANNEL_TIMING_INFO  *ChannelTiming;

  GenericPccCmObj = (CM_ARM_PCC_SUBSPACE_TYPE0_INFO *)PccCmObj;

  if ((PccCmObj == NULL) ||
      ((GenericPccCmObj->Type != EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_3_EXTENDED_PCC) &&
       (GenericPccCmObj->Type != EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_4_EXTENDED_PCC)) ||
      (PccAcpi == NULL))
  {
    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
    return EFI_INVALID_PARAMETER;
  }

  Doorbell          = &GenericPccCmObj->DoorbellReg;
  PlatIrqAck        = &PccCmObj->PlatIrqAckReg;
  CmdCompleteCheck  = &PccCmObj->CmdCompleteCheckReg;
  CmdCompleteUpdate = &PccCmObj->CmdCompleteUpdateReg;
  ErrorStatus       = &PccCmObj->ErrorStatusReg;
  ChannelTiming     = &GenericPccCmObj->ChannelTiming;

  PccAcpi->Type                   = GenericPccCmObj->Type;
  PccAcpi->Length                 = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_3_EXTENDED_PCC);
  PccAcpi->PlatformInterrupt      = PccCmObj->PlatIrq.Interrupt;
  PccAcpi->PlatformInterruptFlags = PccCmObj->PlatIrq.Flags;
  PccAcpi->Reserved               = EFI_ACPI_RESERVED_BYTE;
  PccAcpi->BaseAddress            = GenericPccCmObj->BaseAddress;
  PccAcpi->AddressLength          = GenericPccCmObj->AddressLength;

  CopyMem (
    &PccAcpi->DoorbellRegister,
    &Doorbell->Register,
    sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
    );
  PccAcpi->DoorbellPreserve = Doorbell->PreserveMask;
  PccAcpi->DoorbellWrite    = Doorbell->WriteMask;

  PccAcpi->NominalLatency               = ChannelTiming->NominalLatency;
  PccAcpi->MaximumPeriodicAccessRate    = ChannelTiming->MaxPeriodicAccessRate;
  PccAcpi->MinimumRequestTurnaroundTime = ChannelTiming->MinRequestTurnaroundTime;

  CopyMem (
    &PccAcpi->PlatformInterruptAckRegister,
    &PlatIrqAck->Register,
    sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
    );
  PccAcpi->PlatformInterruptAckPreserve = PlatIrqAck->PreserveMask;
  PccAcpi->PlatformInterruptAckSet      = PlatIrqAck->WriteMask;

  PccAcpi->Reserved1[0] = EFI_ACPI_RESERVED_BYTE;
  PccAcpi->Reserved1[1] = EFI_ACPI_RESERVED_BYTE;
  PccAcpi->Reserved1[1] = EFI_ACPI_RESERVED_BYTE;
  PccAcpi->Reserved1[3] = EFI_ACPI_RESERVED_BYTE;
  PccAcpi->Reserved1[4] = EFI_ACPI_RESERVED_BYTE;
  PccAcpi->Reserved1[5] = EFI_ACPI_RESERVED_BYTE;
  PccAcpi->Reserved1[6] = EFI_ACPI_RESERVED_BYTE;
  PccAcpi->Reserved1[7] = EFI_ACPI_RESERVED_BYTE;

  CopyMem (
    &PccAcpi->CommandCompleteCheckRegister,
    &CmdCompleteCheck->Register,
    sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
    );
  PccAcpi->CommandCompleteCheckMask = CmdCompleteCheck->PreserveMask;
  // No Write mask.

  CopyMem (
    &PccAcpi->CommandCompleteUpdateRegister,
    &CmdCompleteUpdate->Register,
    sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
    );
  PccAcpi->CommandCompleteUpdatePreserve = CmdCompleteUpdate->PreserveMask;
  PccAcpi->CommandCompleteUpdateSet      = CmdCompleteUpdate->WriteMask;

  CopyMem (
    &PccAcpi->ErrorStatusRegister,
    &ErrorStatus->Register,
    sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
    );
  PccAcpi->ErrorStatusMask = ErrorStatus->PreserveMask;
  // No Write mask.

  if (GenericPccCmObj->Type == EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_4_EXTENDED_PCC) {
    mHasPlatformInterrupt = TRUE;
  } else if ((PccCmObj->PlatIrq.Interrupt != 0)) {
    mHasPlatformInterrupt = TRUE;
  }

  return EFI_SUCCESS;
}

/** Add one PCCT subspace structure of Type 5 (HW-Registers).

  @param [in]  PccCmObj   Pointer to a CmObj PCCT Subspace info structure.
  @param [in]  PccAcpi    Pointer to the ACPI PCCT Subspace structure to populate.

  @retval EFI_SUCCESS           Table generated successfully.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
**/
STATIC
EFI_STATUS
EFIAPI
AddSubspaceStructType5 (
  IN  CM_ARM_PCC_SUBSPACE_TYPE5_INFO                            *PccCmObj,
  IN  EFI_ACPI_6_4_PCCT_SUBSPACE_5_HW_REGISTERS_COMMUNICATIONS  *PccAcpi
  )
{
  CM_ARM_PCC_SUBSPACE_TYPE0_INFO    *GenericPccCmObj;
  PCC_MAILBOX_REGISTER_INFO         *Doorbell;
  PCC_MAILBOX_REGISTER_INFO         *CmdCompleteCheck;
  PCC_MAILBOX_REGISTER_INFO         *ErrorStatus;
  PCC_SUBSPACE_CHANNEL_TIMING_INFO  *ChannelTiming;

  GenericPccCmObj = (CM_ARM_PCC_SUBSPACE_TYPE0_INFO *)PccCmObj;

  if ((PccCmObj == NULL) ||
      (GenericPccCmObj->Type != EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_5_HW_REGISTERS_COMMUNICATIONS)  ||
      (PccAcpi == NULL))
  {
    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
    return EFI_INVALID_PARAMETER;
  }

  Doorbell         = &GenericPccCmObj->DoorbellReg;
  CmdCompleteCheck = &PccCmObj->CmdCompleteCheckReg;
  ErrorStatus      = &PccCmObj->ErrorStatusReg;
  ChannelTiming    = &GenericPccCmObj->ChannelTiming;

  PccAcpi->Type                    = GenericPccCmObj->Type;
  PccAcpi->Length                  = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_5_HW_REGISTERS_COMMUNICATIONS);
  PccAcpi->Version                 = PccCmObj->Version;
  PccAcpi->BaseAddress             = GenericPccCmObj->BaseAddress;
  PccAcpi->SharedMemoryRangeLength = GenericPccCmObj->AddressLength;

  CopyMem (
    &PccAcpi->DoorbellRegister,
    &Doorbell->Register,
    sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
    );
  PccAcpi->DoorbellPreserve = Doorbell->PreserveMask;
  PccAcpi->DoorbellWrite    = Doorbell->WriteMask;

  CopyMem (
    &PccAcpi->CommandCompleteCheckRegister,
    &CmdCompleteCheck->Register,
    sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
    );
  PccAcpi->CommandCompleteCheckMask = CmdCompleteCheck->PreserveMask;
  // No Write mask.

  CopyMem (
    &PccAcpi->ErrorStatusRegister,
    &ErrorStatus->Register,
    sizeof (EFI_ACPI_6_4_GENERIC_ADDRESS_STRUCTURE)
    );
  PccAcpi->ErrorStatusMask = ErrorStatus->PreserveMask;
  // No Write mask.

  PccAcpi->NominalLatency = ChannelTiming->NominalLatency;
  // No MaximumPeriodicAccessRate.
  PccAcpi->MinimumRequestTurnaroundTime = ChannelTiming->MinRequestTurnaroundTime;

  return EFI_SUCCESS;
}

/** Populate the PCCT table using the MappingTable.

  @param [in] MappingTable  The mapping table structure.
  @param [in] Pcc           Pointer to an array of Pcc Subpace structures.
  @param [in] Size          Size of the Pcc array.

  @retval EFI_SUCCESS           Table generated successfully.
  @retval EFI_BUFFER_TOO_SMALL  Buffer too small.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
**/
STATIC
EFI_STATUS
EFIAPI
PopulatePcctTable (
  IN  MAPPING_TABLE  *MappingTable,
  IN  VOID           *Pcc,
  IN  UINT32         Size
  )
{
  EFI_STATUS  Status;
  UINT8       *PccBuffer;
  UINT32      CmObjSize;
  UINT32      Index;
  UINT32      MaxIndex;
  VOID        **Table;
  VOID        *CurrentPccSubspace;

  ASSERT (MappingTable != NULL);
  ASSERT (MappingTable->Table != NULL);

  PccBuffer = Pcc;
  MaxIndex  = MappingTable->MaxIndex;
  Table     = MappingTable->Table;

  for (Index = 0; Index < MaxIndex; Index++) {
    CurrentPccSubspace = Table[Index];
    if (CurrentPccSubspace == NULL) {
      ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
      return EFI_INVALID_PARAMETER;
    }

    switch (((PCC_SUBSPACE_GENERIC_INFO *)CurrentPccSubspace)->Type) {
      case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_GENERIC:
        Status = AddSubspaceStructType0 (
                   (CM_ARM_PCC_SUBSPACE_TYPE0_INFO *)CurrentPccSubspace,
                   (EFI_ACPI_6_4_PCCT_SUBSPACE_GENERIC *)PccBuffer
                   );

        CmObjSize = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_GENERIC);
        break;

      case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_1_HW_REDUCED_COMMUNICATIONS:
        Status = AddSubspaceStructType1 (
                   (CM_ARM_PCC_SUBSPACE_TYPE1_INFO *)CurrentPccSubspace,
                   (EFI_ACPI_6_4_PCCT_SUBSPACE_1_HW_REDUCED_COMMUNICATIONS *)PccBuffer
                   );

        CmObjSize = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_1_HW_REDUCED_COMMUNICATIONS);
        break;

      case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_2_HW_REDUCED_COMMUNICATIONS:
        Status = AddSubspaceStructType2 (
                   (CM_ARM_PCC_SUBSPACE_TYPE2_INFO *)CurrentPccSubspace,
                   (EFI_ACPI_6_4_PCCT_SUBSPACE_2_HW_REDUCED_COMMUNICATIONS *)PccBuffer
                   );

        CmObjSize = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_2_HW_REDUCED_COMMUNICATIONS);
        break;

      case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_3_EXTENDED_PCC:
        Status = AddSubspaceStructType34 (
                   (CM_ARM_PCC_SUBSPACE_TYPE3_INFO *)CurrentPccSubspace,
                   (EFI_ACPI_6_4_PCCT_SUBSPACE_3_EXTENDED_PCC *)PccBuffer
                   );

        CmObjSize = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_3_EXTENDED_PCC);
        break;

      case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_4_EXTENDED_PCC:
        Status = AddSubspaceStructType34 (
                   (CM_ARM_PCC_SUBSPACE_TYPE4_INFO *)CurrentPccSubspace,
                   (EFI_ACPI_6_4_PCCT_SUBSPACE_4_EXTENDED_PCC *)PccBuffer
                   );

        CmObjSize = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_4_EXTENDED_PCC);
        break;

      case EFI_ACPI_6_4_PCCT_SUBSPACE_TYPE_5_HW_REGISTERS_COMMUNICATIONS:
        Status = AddSubspaceStructType5 (
                   (CM_ARM_PCC_SUBSPACE_TYPE5_INFO *)CurrentPccSubspace,
                   (EFI_ACPI_6_4_PCCT_SUBSPACE_5_HW_REGISTERS_COMMUNICATIONS *)PccBuffer
                   );

        CmObjSize = sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_5_HW_REGISTERS_COMMUNICATIONS);
        break;

      default:
        ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
        return EFI_INVALID_PARAMETER;
    } // switch

    if (EFI_ERROR (Status)) {
      ASSERT_EFI_ERROR (Status);
      return Status;
    }

    if (Size < CmObjSize) {
      ASSERT_EFI_ERROR (EFI_BUFFER_TOO_SMALL);
      return EFI_BUFFER_TOO_SMALL;
    }

    PccBuffer += CmObjSize;
    Size      -= CmObjSize;
  } // for

  return EFI_SUCCESS;
}

/** Construct the PCCT ACPI table.

  Called by the Dynamic Table Manager, 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.
  @retval EFI_BUFFER_TOO_SMALL   Buffer too small.
  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
**/
STATIC
EFI_STATUS
EFIAPI
BuildPcctTable (
  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;
  ACPI_PCCT_GENERATOR                                       *Generator;
  UINT32                                                    TableSize;
  EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER  *Pcct;
  UINT8                                                     *Buffer;

  MAPPING_TABLE  *MappingTable;
  UINT32         MappingTableCount;

  CM_ARM_PCC_SUBSPACE_TYPE0_INFO  *PccType0;
  UINT32                          PccType0Count;
  CM_ARM_PCC_SUBSPACE_TYPE1_INFO  *PccType1;
  UINT32                          PccType1Count;
  CM_ARM_PCC_SUBSPACE_TYPE2_INFO  *PccType2;
  UINT32                          PccType2Count;
  CM_ARM_PCC_SUBSPACE_TYPE3_INFO  *PccType3;
  UINT32                          PccType3Count;
  CM_ARM_PCC_SUBSPACE_TYPE4_INFO  *PccType4;
  UINT32                          PccType4Count;
  CM_ARM_PCC_SUBSPACE_TYPE5_INFO  *PccType5;
  UINT32                          PccType5Count;

  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: PCCT: Requested table revision = %d, is not supported."
      "Supported table revision: Minimum = %d, Maximum = %d\n",
      AcpiTableInfo->AcpiTableRevision,
      This->MinAcpiTableRevision,
      This->AcpiTableRevision
      ));
    ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
    return EFI_INVALID_PARAMETER;
  }

  Generator    = (ACPI_PCCT_GENERATOR *)This;
  MappingTable = &Generator->MappingTable;
  *Table       = NULL;

  // First get all the Pcc Subpace CmObj of type X.

  Status = GetEArmObjPccSubspaceType0Info (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &PccType0,
             &PccType0Count
             );
  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  Status = GetEArmObjPccSubspaceType1Info (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &PccType1,
             &PccType1Count
             );
  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  Status = GetEArmObjPccSubspaceType2Info (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &PccType2,
             &PccType2Count
             );
  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  Status = GetEArmObjPccSubspaceType3Info (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &PccType3,
             &PccType3Count
             );
  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  Status = GetEArmObjPccSubspaceType4Info (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &PccType4,
             &PccType4Count
             );
  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  Status = GetEArmObjPccSubspaceType5Info (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &PccType5,
             &PccType5Count
             );
  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  // Count the number of Pcc Subspaces.
  MappingTableCount  = PccType0Count;
  MappingTableCount += PccType1Count;
  MappingTableCount += PccType2Count;
  MappingTableCount += PccType3Count;
  MappingTableCount += PccType4Count;
  MappingTableCount += PccType5Count;

  Status = MappingTableInitialize (MappingTable, MappingTableCount);
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  // Map the Subspace Ids for all types.

  Status = MapPccSubspaceId (MappingTable, PccType0, PccType0Count);
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  Status = MapPccSubspaceId (MappingTable, PccType1, PccType1Count);
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  Status = MapPccSubspaceId (MappingTable, PccType2, PccType2Count);
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  Status = MapPccSubspaceId (MappingTable, PccType3, PccType3Count);
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  Status = MapPccSubspaceId (MappingTable, PccType4, PccType4Count);
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  Status = MapPccSubspaceId (MappingTable, PccType5, PccType5Count);
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  // Compute the size of the PCCT table.
  TableSize  = sizeof (EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER);
  TableSize += PccType0Count * sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_GENERIC);
  TableSize += PccType1Count * sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_1_HW_REDUCED_COMMUNICATIONS);
  TableSize += PccType2Count * sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_2_HW_REDUCED_COMMUNICATIONS);
  TableSize += PccType3Count * sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_3_EXTENDED_PCC);
  TableSize += PccType4Count * sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_4_EXTENDED_PCC);
  TableSize += PccType5Count * sizeof (EFI_ACPI_6_4_PCCT_SUBSPACE_5_HW_REGISTERS_COMMUNICATIONS);

  // Allocate a Buffer for the PCCT table.
  *Table = (EFI_ACPI_DESCRIPTION_HEADER *)AllocateZeroPool (TableSize);
  if (*Table == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  Pcct = (EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER *)*Table;

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

  Buffer     = (UINT8 *)Pcct;
  Buffer    += sizeof (EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER);
  TableSize -= sizeof (EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER);

  // Populate the PCCT table by following the Subspace Id mapping.
  Status = PopulatePcctTable (MappingTable, Buffer, TableSize);
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
    goto error_handler;
  }

  // Setup the Reserved fields once mHasPlatformInterrupt hase been populated.
  Pcct->Flags    = mHasPlatformInterrupt;
  Pcct->Reserved = EFI_ACPI_RESERVED_QWORD;

  MappingTableFree (MappingTable);

  return Status;

error_handler:
  DEBUG ((
    DEBUG_ERROR,
    "ERROR: PCCT: Failed to install table. Status = %r\n",
    Status
    ));

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

  MappingTableFree (MappingTable);

  return Status;
}

/** Free any resources allocated for constructing the PCCT.

  @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
FreePcctTableResources (
  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
  )
{
  ASSERT (This != NULL);
  ASSERT (AcpiTableInfo != NULL);
  ASSERT (CfgMgrProtocol != NULL);
  ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
  ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);

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

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

/** This macro defines the PCCT Table Generator revision.
*/
#define PCCT_GENERATOR_REVISION  CREATE_REVISION (1, 0)

/** The interface for the PCCT Table Generator.
*/
STATIC
ACPI_PCCT_GENERATOR  PcctGenerator = {
  // ACPI table generator header
  {
    // Generator ID
    CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdPcct),
    // Generator Description
    L"ACPI.STD.PCCT.GENERATOR",
    // ACPI Table Signature
    EFI_ACPI_6_4_PLATFORM_COMMUNICATIONS_CHANNEL_TABLE_SIGNATURE,
    // ACPI Table Revision supported by this Generator
    EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_REVISION,
    // Minimum ACPI Table Revision supported by this Generator
    EFI_ACPI_6_4_PLATFORM_COMMUNICATION_CHANNEL_TABLE_REVISION,
    // Creator ID
    TABLE_GENERATOR_CREATOR_ID_ARM,
    // Creator Revision
    PCCT_GENERATOR_REVISION,
    // Build Table function
    BuildPcctTable,
    // Free Resource function
    FreePcctTableResources,
    // Extended build function not needed
    NULL,
    // Extended build function not implemented by the generator.
    // Hence extended free resource function is not required.
    NULL
  },

  // Private fields are defined from here.

  // Mapping Table
  {
    // Table
    NULL,
    // MaxIndex
    0,
  },
};

/** 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
AcpiPcctLibConstructor (
  IN  EFI_HANDLE        ImageHandle,
  IN  EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  Status = RegisterAcpiTableGenerator (&PcctGenerator.Header);
  DEBUG ((DEBUG_INFO, "PCCT: 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
AcpiPcctLibDestructor (
  IN  EFI_HANDLE        ImageHandle,
  IN  EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

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