Przeglądaj źródła

DynamicTablesPkg: Add PCCT Generator

The Platform Communication Channel Table (PCCT) generator collates
the relevant information required for generating a PCCT table from
configuration manager using the configuration manager protocol.
The DynamicTablesManager then install the PCCT table.

From ACPI 6.4, s14 PLATFORM COMMUNICATIONS CHANNEL (PCC):
  The platform communication channel (PCC) is a generic mechanism
  for OSPM to communicate with an entity in the platform.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Reviewed-by: Sami Mujawar <sami.mujawar@arm.com>
Pierre Gondois 1 rok temu
rodzic
commit
8405b1480f

+ 3 - 1
DynamicTablesPkg/DynamicTables.dsc.inc

@@ -1,7 +1,7 @@
 ## @file
 #  Dsc include file for Dynamic Tables Framework.
 #
-#  Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR>
+#  Copyright (c) 2017 - 2022, Arm Limited. All rights reserved.<BR>
 #
 #  SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -33,6 +33,7 @@
   DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/AcpiRawLibArm.inf
   DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf
   DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/AcpiSratLibArm.inf
+  DynamicTablesPkg/Library/Acpi/Arm/AcpiPcctLibArm/AcpiPcctLibArm.inf
 
   # AML Fixup
   DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf
@@ -57,6 +58,7 @@
       NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/AcpiRawLibArm.inf
       NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf
       NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSratLibArm/AcpiSratLibArm.inf
+      NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiPcctLibArm/AcpiPcctLibArm.inf
 
       # AML Fixup
       NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf

+ 2 - 1
DynamicTablesPkg/Include/AcpiTableGenerator.h

@@ -1,6 +1,6 @@
 /** @file
 
-  Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR>
+  Copyright (c) 2017 - 2022, Arm Limited. All rights reserved.<BR>
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 
@@ -98,6 +98,7 @@ typedef enum StdAcpiTableId {
   EStdAcpiTableIdSsdtCmn600,                    ///< SSDT Cmn-600 Generator
   EStdAcpiTableIdSsdtCpuTopology,               ///< SSDT Cpu Topology
   EStdAcpiTableIdSsdtPciExpress,                ///< SSDT Pci Express Generator
+  EStdAcpiTableIdPcct,                          ///< PCCT Generator
   EStdAcpiTableIdMax
 } ESTD_ACPI_TABLE_ID;
 

+ 30 - 0
DynamicTablesPkg/Library/Acpi/Arm/AcpiPcctLibArm/AcpiPcctLibArm.inf

@@ -0,0 +1,30 @@
+## @file
+#  Pcct Table Generator
+#
+#  Copyright (c) 2022, Arm Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION    = 0x0001001B
+  BASE_NAME      = AcpiPcctLibArm
+  FILE_GUID      = 38FE945C-D6ED-4CD6-8D20-FCEF3260D15A
+  VERSION_STRING = 1.0
+  MODULE_TYPE    = DXE_DRIVER
+  LIBRARY_CLASS  = NULL|DXE_DRIVER
+  CONSTRUCTOR    = AcpiPcctLibConstructor
+  DESTRUCTOR     = AcpiPcctLibDestructor
+
+[Sources]
+  PcctGenerator.c
+  PcctGenerator.h
+
+[Packages]
+  DynamicTablesPkg/DynamicTablesPkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+  BaseLib

+ 1186 - 0
DynamicTablesPkg/Library/Acpi/Arm/AcpiPcctLibArm/PcctGenerator.c

@@ -0,0 +1,1186 @@
+/** @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;
+}

+ 43 - 0
DynamicTablesPkg/Library/Acpi/Arm/AcpiPcctLibArm/PcctGenerator.h

@@ -0,0 +1,43 @@
+/** @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)
+
+**/
+
+#ifndef PCCT_GENERATOR_H_
+#define PCCT_GENERATOR_H_
+
+#pragma pack(1)
+
+/** Structure used to map a Pcc Subspace to an index.
+*/
+typedef struct MappingTable {
+  /// Mapping table for Subspace Ids.
+  /// Subspace ID/Index <-> CM_ARM_PCC_SUBSPACE_TYPE[X]_INFO pointer
+  VOID      **Table;
+
+  /// Number of entries in the Table.
+  UINT32    MaxIndex;
+} MAPPING_TABLE;
+
+/** A structure holding the Pcct generator and additional private data.
+*/
+typedef struct AcpiPcctGenerator {
+  /// ACPI Table generator header
+  ACPI_TABLE_GENERATOR    Header;
+
+  // Private fields are defined from here.
+
+  /// Table to map: Subspace ID/Index <-> CM_ARM_PCC_SUBSPACE_TYPE[X]_INFO pointer
+  MAPPING_TABLE           MappingTable;
+} ACPI_PCCT_GENERATOR;
+
+#pragma pack()
+
+#endif // PCCT_GENERATOR_H_