[edk2-devel] [PATCH v1 13/13] DynamicTablesPkg: SSDT CPU topology and LPI state generator

PierreGondois posted 13 patches 4 years, 7 months ago
There is a newer version of this series
[edk2-devel] [PATCH v1 13/13] DynamicTablesPkg: SSDT CPU topology and LPI state generator
Posted by PierreGondois 4 years, 7 months ago
From: Pierre Gondois <Pierre.Gondois@arm.com>

In the GIC interrupt model, logical processors are required to
have a Processor Device object in the DSDT and must convey each
processor's GIC information to the OS using the GICC structure.
Additionally, _LPI objects may be needed as they provide a method
to describe Low Power Idle states that defines the local power
states for each node in a hierarchical processor topology.

Therefore, add support to generate the CPU topology and the LPI
state information in an SSDT table.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
---
 DynamicTablesPkg/DynamicTables.dsc.inc        |    6 +
 DynamicTablesPkg/Include/AcpiTableGenerator.h |    7 +-
 .../SsdtCpuTopologyGenerator.c                | 1230 +++++++++++++++++
 .../SsdtCpuTopologyGenerator.h                |  134 ++
 .../SsdtCpuTopologyLibArm.inf                 |   40 +
 5 files changed, 1416 insertions(+), 1 deletion(-)
 create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.c
 create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.h
 create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf

diff --git a/DynamicTablesPkg/DynamicTables.dsc.inc b/DynamicTablesPkg/DynamicTables.dsc.inc
index ed221d1681eb..292215c39456 100644
--- a/DynamicTablesPkg/DynamicTables.dsc.inc
+++ b/DynamicTablesPkg/DynamicTables.dsc.inc
@@ -37,6 +37,9 @@ [Components.common]
   DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf
   DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf
 
+  # AML Codegen
+  DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf
+
   #
   # Dynamic Table Factory Dxe
   #
@@ -56,6 +59,9 @@ [Components.common]
       # AML Fixup
       NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf
       NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf
+
+      # AML Codegen
+      NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf
   }
 
   #
diff --git a/DynamicTablesPkg/Include/AcpiTableGenerator.h b/DynamicTablesPkg/Include/AcpiTableGenerator.h
index 352331d6dc95..45c808ba740d 100644
--- a/DynamicTablesPkg/Include/AcpiTableGenerator.h
+++ b/DynamicTablesPkg/Include/AcpiTableGenerator.h
@@ -1,6 +1,6 @@
 /** @file
 
-  Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
+  Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR>
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 
@@ -63,6 +63,10 @@ The Dynamic Tables Framework implements the following ACPI table generators:
             The SSDT CMN-600 generator collates the CMN-600 information
             from the Configuration Manager and patches the SSDT CMN-600
             template to build the SSDT CMN-600 table.
+  - SSDT Cpu-Topology:
+            The SSDT Cpu-Topology generator collates the cpu and LPI
+            information from the Configuration Manager and generates a
+            SSDT table describing the CPU hierarchy.
 */
 
 /** The ACPI_TABLE_GENERATOR_ID type describes ACPI table generator ID.
@@ -88,6 +92,7 @@ typedef enum StdAcpiTableId {
   EStdAcpiTableIdSrat,                          ///< SRAT Generator
   EStdAcpiTableIdSsdtSerialPort,                ///< SSDT Serial-Port Generator
   EStdAcpiTableIdSsdtCmn600,                    ///< SSDT Cmn-600 Generator
+  EStdAcpiTableIdSsdtCpuTopology,               ///< SSDT Cpu Topology
   EStdAcpiTableIdMax
 } ESTD_ACPI_TABLE_ID;
 
diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.c b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.c
new file mode 100644
index 000000000000..88db808760f7
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.c
@@ -0,0 +1,1230 @@
+/** @file
+  SSDT Cpu Topology Table Generator.
+
+  Copyright (c) 2021, Arm Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+    - ACPI 6.3 Specification - January 2019 - s8.4 Declaring Processors
+**/
+
+#include <IndustryStandard/DebugPort2Table.h>
+#include <Library/AcpiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/AcpiTable.h>
+
+// Module specific include files.
+#include <AcpiTableGenerator.h>
+#include <ConfigurationManagerObject.h>
+#include <ConfigurationManagerHelper.h>
+#include <Library/AcpiHelperLib.h>
+#include <Library/AmlLib/AmlLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+#include "SsdtCpuTopologyGenerator.h"
+
+/** ARM standard SSDT Cpu Topology Table Generator.
+
+Requirements:
+  The following Configuration Manager Object(s) are required by
+  this Generator:
+  - EArmObjProcHierarchyInfo
+  - EArmObjGicCInfo
+  - EArmObjCmRef
+  - EArmObjLpiInfo
+*/
+
+/** This macro expands to a function that retrieves the GIC
+    CPU interface Information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+  EObjNameSpaceArm,
+  EArmObjGicCInfo,
+  CM_ARM_GICC_INFO
+  );
+
+/**
+  This macro expands to a function that retrieves the Processor Hierarchy
+  information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+  EObjNameSpaceArm,
+  EArmObjProcHierarchyInfo,
+  CM_ARM_PROC_HIERARCHY_INFO
+  );
+
+/**
+  This macro expands to a function that retrieves the cross-CM-object-
+  reference information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+  EObjNameSpaceArm,
+  EArmObjCmRef,
+  CM_ARM_OBJ_REF
+  );
+
+/**
+  This macro expands to a function that retrieves the Lpi
+  information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+  EObjNameSpaceArm,
+  EArmObjLpiInfo,
+  CM_ARM_LPI_INFO
+  );
+
+/** Initialize the TokenTable.
+
+  One entry should be allocated for each CM_ARM_PROC_HIERARCHY_INFO
+  structure of the platform. The TokenTable allows to have a mapping:
+  Index <-> CM_OBJECT_TOKEN (to CM_ARM_LPI_INFO structures).
+
+  There will always be less sets of Lpi states (CM_ARM_OBJ_REF)
+  than the number of cpus/clusters (CM_ARM_PROC_HIERARCHY_INFO).
+
+  @param [in]  Generator  The SSDT Cpu Topology generator.
+  @param [in]  Count      Number of entries to allocate in the TokenTable.
+
+  @retval EFI_SUCCESS            Success.
+  @retval EFI_INVALID_PARAMETER  Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+TokenTableInitialize (
+  IN  ACPI_CPU_TOPOLOGY_GENERATOR   * Generator,
+  IN  UINT32                          Count
+  )
+{
+  CM_OBJECT_TOKEN   * Table;
+
+  if ((Generator == NULL) ||
+      (Count == 0)        ||
+      (Count >= MAX_INDEX_NAME)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Table = AllocateZeroPool (sizeof (CM_OBJECT_TOKEN) * Count);
+  if (Table == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Generator->TokenTable.Table = Table;
+
+  return EFI_SUCCESS;
+}
+
+/** Free the TokenTable.
+
+  @param [in]  Generator    The SSDT Cpu Topology generator.
+**/
+STATIC
+VOID
+EFIAPI
+TokenTableFree (
+  IN  ACPI_CPU_TOPOLOGY_GENERATOR   * Generator
+  )
+{
+  ASSERT (Generator != NULL);
+  ASSERT (Generator->TokenTable.Table != NULL);
+
+  if (Generator->TokenTable.Table != NULL) {
+    FreePool (Generator->TokenTable.Table);
+  }
+}
+
+/** Add a new entry to the TokenTable and return its index.
+
+  If an entry with Token is already available in the table,
+  return its index without adding a new entry.
+
+  @param [in]  Generator  The SSDT Cpu Topology generator.
+  @param [in]  Token      New Token entry to add.
+
+  @retval The index of the token entry in the TokenTable.
+**/
+STATIC
+UINT32
+EFIAPI
+TokenTableAdd (
+  IN  ACPI_CPU_TOPOLOGY_GENERATOR   * Generator,
+  IN  CM_OBJECT_TOKEN                 Token
+  )
+{
+  CM_OBJECT_TOKEN   * Table;
+  UINT32              Index;
+  UINT32              LastIndex;
+
+  ASSERT (Generator != NULL);
+  ASSERT (Generator->TokenTable.Table != NULL);
+
+  Table = Generator->TokenTable.Table;
+  LastIndex = Generator->TokenTable.LastIndex;
+
+  // Search if there is already an entry with this Token.
+  for (Index = 0; Index < LastIndex; Index++) {
+    if (Table[Index] == Token) {
+      return Index;
+    }
+  }
+
+  ASSERT (LastIndex < MAX_INDEX_NAME);
+  ASSERT (LastIndex < Generator->ProcNodeCount);
+
+  // If no, create a new entry.
+  Table[LastIndex] = Token;
+
+  return Generator->TokenTable.LastIndex++;
+}
+
+/** Write a string 'Xxxx\0' in AslName (5 bytes long),
+  with 'X' being the leading char of the name, and
+  with 'xxx' being Value in hexadecimal.
+
+  As 'xxx' in hexadecimal represents a number on 12 bits,
+  we have Value < (2 << 12)
+
+  @param [in]       LeadChar  Leading char of the name.
+  @param [in]       Value     Hex value of the name.
+                              Must be lower than (2 << 12).
+  @param [in, out]  AslName   Pointer to write the 'Xxxx' string to.
+                              Must be at least 5 bytes long.
+
+  @retval EFI_SUCCESS               Success.
+  @retval EFI_INVALID_PARAMETER     Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+WriteAslName (
+  IN      CHAR8     LeadChar,
+  IN      UINT32    Value,
+  IN OUT  CHAR8   * AslName
+  )
+{
+  UINT8   Index;
+
+  if ((Value >= MAX_INDEX_NAME)  ||
+      (AslName == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  AslName[0] = LeadChar;
+  AslName[AML_NAME_SEG_SIZE] = '\0';
+
+  for (Index = 0; Index < AML_NAME_SEG_SIZE - 1; Index++) {
+    AslName[AML_NAME_SEG_SIZE - Index - 1] =
+      AsciiFromHex (((Value >> (4 * Index)) & 0xF));
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** Create and add an _LPI method to Cpu/Cluster Node.
+
+  For instance, transform an AML node from:
+  Device (C002)
+  {
+      Name (_UID, 2)
+      Name (_HID, "ACPI0007")
+  }
+
+  To:
+  Device (C002)
+  {
+      Name (_UID, 2)
+      Name (_HID, "ACPI0007")
+      Method (_LPI, 0, NotSerialized)
+      {
+          Return (\_SB.L003)
+      }
+  }
+
+  @param [in]  Generator              The SSDT Cpu Topology generator.
+  @param [in]  ProcHierarchyNodeInfo  CM_ARM_PROC_HIERARCHY_INFO describing
+                                      the Cpu.
+  @param [in]  Node                   Node to which the _LPI method is
+                                      attached. Can represent a Cpu or a
+                                      Cluster.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlLpiMethod (
+  IN  ACPI_CPU_TOPOLOGY_GENERATOR   * Generator,
+  IN  CM_ARM_PROC_HIERARCHY_INFO    * ProcHierarchyNodeInfo,
+  IN  AML_OBJECT_NODE_HANDLE        * Node
+  )
+{
+  EFI_STATUS    Status;
+  UINT32        TokenIndex;
+  CHAR8         AslName[SB_SCOPE_PREFIX_SIZE + AML_NAME_SEG_SIZE];
+
+  ASSERT (Generator != NULL);
+  ASSERT (ProcHierarchyNodeInfo != NULL);
+  ASSERT (ProcHierarchyNodeInfo->LpiToken != CM_NULL_TOKEN);
+  ASSERT (Node != NULL);
+
+  TokenIndex = TokenTableAdd (Generator, ProcHierarchyNodeInfo->LpiToken);
+
+  CopyMem (AslName, SB_SCOPE_PREFIX, SB_SCOPE_PREFIX_SIZE);
+
+  Status = WriteAslName (
+             'L',
+             TokenIndex,
+             AslName + SB_SCOPE_PREFIX_SIZE - 1
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // ASL:
+  // Method (_LPI, 0) {
+  //   Return ([AslName])
+  // }
+  Status = AmlCodeGenMethodRetNameString (
+             "_LPI",
+             AslName,
+             0,
+             FALSE,
+             0,
+             Node,
+             NULL
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+
+  return Status;
+}
+
+/** Generate all the Lpi states under the '_SB' scope.
+
+  This function generates the following ASL code:
+  Scope (\_SB) {
+    Name (L000, Package() {
+      0, // Version
+      0, // Level Index
+      X, // Count
+      Package() {
+        [An Lpi state]
+      },
+      Package() {
+        [Another Lpi state]
+      },
+    } // Name L000
+
+    Name (L001, Package() {
+      ...
+    } // Name L001
+
+    ...
+  } // Scope /_SB
+
+  The Lpi states are fetched from the Configuration Manager.
+  The names of the Lpi states are generated from the TokenTable.
+
+  @param [in]  Generator        The SSDT Cpu Topology generator.
+  @param [in]  CfgMgrProtocol   Pointer to the Configuration Manager
+                                Protocol Interface.
+  @param [in] ScopeNode         Scope node handle ('\_SB' scope).
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GenerateLpiStates (
+  IN        ACPI_CPU_TOPOLOGY_GENERATOR           *       Generator,
+  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
+  IN        AML_OBJECT_NODE_HANDLE                        ScopeNode
+  )
+{
+  EFI_STATUS                Status;
+
+  UINT32                    Index;
+  UINT32                    LastIndex;
+
+  AML_OBJECT_NODE_HANDLE    LpiNode;
+  CM_ARM_OBJ_REF          * LpiRefInfo;
+  UINT32                    LpiRefInfoCount;
+  UINT32                    LpiRefIndex;
+  CM_ARM_LPI_INFO         * LpiInfo;
+  CHAR8                     AslName[AML_NAME_SEG_SIZE + 1];
+
+  ASSERT (Generator != NULL);
+  ASSERT (Generator->TokenTable.Table != NULL);
+  ASSERT (CfgMgrProtocol != NULL);
+  ASSERT (ScopeNode != NULL);
+
+  LastIndex = Generator->TokenTable.LastIndex;
+
+  // For each entry in the TokenTable, create a name in the AML namespace
+  // under SB_SCOPE, to store the Lpi states associated with the LpiToken.
+  for (Index = 0; Index < LastIndex; Index++) {
+    Status = WriteAslName ('L', Index, AslName);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // We do not support the LevelId field for now, let it to 0.
+    Status = AmlCreateLpiNode (AslName, 1, 0, ScopeNode, &LpiNode);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // Fetch the LPI objects referenced by the token.
+    Status = GetEArmObjCmRef (
+               CfgMgrProtocol,
+               Generator->TokenTable.Table[Index],
+               &LpiRefInfo,
+               &LpiRefInfoCount
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    for (LpiRefIndex = 0; LpiRefIndex < LpiRefInfoCount; LpiRefIndex++) {
+      // For each CM_ARM_LPI_INFO referenced by the token, add an Lpi state.
+      Status = GetEArmObjLpiInfo (
+                 CfgMgrProtocol,
+                 LpiRefInfo[LpiRefIndex].ReferenceToken,
+                 &LpiInfo,
+                 NULL
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        return Status;
+      }
+
+      Status = AmlAddLpiState (
+                 LpiInfo->MinResidency,
+                 LpiInfo->WorstCaseWakeLatency,
+                 LpiInfo->Flags,
+                 LpiInfo->ArchFlags,
+                 LpiInfo->ResCntFreq,
+                 LpiInfo->EnableParentState,
+                 LpiInfo->IsInteger ?
+                   NULL :
+                   &LpiInfo->RegisterEntryMethod,
+                 LpiInfo->IsInteger ?
+                   LpiInfo->IntegerEntryMethod :
+                   0,
+                 &LpiInfo->ResidencyCounterRegister,
+                 &LpiInfo->UsageCounterRegister,
+                 LpiInfo->StateName,
+                 LpiNode
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        return Status;
+      }
+    } // for LpiRefIndex
+  } // for Index
+
+  return EFI_SUCCESS;
+}
+
+/** Create a Cpu in the AML namespace.
+
+  This generates the following ASL code:
+  Device (C002)
+  {
+      Name (_UID, 2)
+      Name (_HID, "ACPI0007")
+  }
+
+  @param [in]  Generator    The SSDT Cpu Topology generator.
+  @param [in]  ParentNode   Parent node to attach the Cpu node to.
+  @param [in]  GicCInfo     CM_ARM_GICC_INFO object used to create the node.
+  @param [in]  CpuIndex     Index used to generate the node name.
+  @param [out] CpuNodePtr   If not NULL, return the created Cpu node.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlCpu (
+  IN   ACPI_CPU_TOPOLOGY_GENERATOR   * Generator,
+  IN   AML_NODE_HANDLE                 ParentNode,
+  IN   CM_ARM_GICC_INFO              * GicCInfo,
+  IN   UINT32                          CpuIndex,
+  OUT  AML_OBJECT_NODE_HANDLE        * CpuNodePtr OPTIONAL
+  )
+{
+  EFI_STATUS                Status;
+  AML_OBJECT_NODE_HANDLE    CpuNode;
+  CHAR8                     AslName[AML_NAME_SEG_SIZE + 1];
+
+  ASSERT (Generator != NULL);
+  ASSERT (ParentNode != NULL);
+  ASSERT (GicCInfo != NULL);
+
+  Status = WriteAslName ('C', CpuIndex, AslName);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Status = AmlCodeGenDevice (AslName, ParentNode, &CpuNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Status = AmlCodeGenNameInteger (
+             "_UID",
+             GicCInfo->AcpiProcessorUid,
+             CpuNode,
+             NULL
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Status = AmlCodeGenNameString (
+             "_HID",
+             ACPI_HID_PROCESSOR_DEVICE,
+             CpuNode,
+             NULL
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // If requested, return the handle to the CpuNode.
+  if (CpuNodePtr != NULL) {
+    *CpuNodePtr = CpuNode;
+  }
+
+  return Status;
+}
+
+/** Create a Cpu in the AML namespace from a CM_ARM_PROC_HIERARCHY_INFO
+    CM object.
+
+  @param [in]  Generator              The SSDT Cpu Topology generator.
+  @param [in]  CfgMgrProtocol         Pointer to the Configuration Manager
+                                      Protocol Interface.
+  @param [in]  ParentNode             Parent node to attach the Cpu node to.
+  @param [in]  CpuIndex               Index used to generate the node name.
+  @param [in]  ProcHierarchyNodeInfo  CM_ARM_PROC_HIERARCHY_INFO describing
+                                      the Cpu.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlCpuFromProcHierarchy (
+  IN        ACPI_CPU_TOPOLOGY_GENERATOR           *       Generator,
+  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
+  IN        AML_NODE_HANDLE                               ParentNode,
+  IN        UINT32                                        CpuIndex,
+  IN        CM_ARM_PROC_HIERARCHY_INFO            *       ProcHierarchyNodeInfo
+  )
+{
+  EFI_STATUS                Status;
+  CM_ARM_GICC_INFO        * GicCInfo;
+  AML_OBJECT_NODE_HANDLE    CpuNode;
+
+  ASSERT (Generator != NULL);
+  ASSERT (CfgMgrProtocol != NULL);
+  ASSERT (ParentNode != NULL);
+  ASSERT (ProcHierarchyNodeInfo != NULL);
+  ASSERT (ProcHierarchyNodeInfo->GicCToken != CM_NULL_TOKEN);
+
+  Status = GetEArmObjGicCInfo (
+             CfgMgrProtocol,
+             ProcHierarchyNodeInfo->GicCToken,
+             &GicCInfo,
+             NULL
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Status = CreateAmlCpu (Generator, ParentNode, GicCInfo, CpuIndex, &CpuNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // If a set of Lpi states is associated with the
+  // CM_ARM_PROC_HIERARCHY_INFO, create an _LPI method returning them.
+  if (ProcHierarchyNodeInfo->LpiToken != CM_NULL_TOKEN) {
+    Status = CreateAmlLpiMethod (Generator, ProcHierarchyNodeInfo, CpuNode);
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  return Status;
+}
+
+/** Create a Cluster in the AML namespace.
+
+  Any CM_ARM_PROC_HIERARCHY_INFO object with the following flags is
+  assumed to be a cluster:
+   - EFI_ACPI_6_3_PPTT_PACKAGE_NOT_PHYSICAL
+   - EFI_ACPI_6_3_PPTT_PROCESSOR_ID_INVALID
+   - EFI_ACPI_6_3_PPTT_NODE_IS_NOT_LEAF
+
+  This generates the following ASL code:
+  Device (C002)
+  {
+      Name (_UID, 2)
+      Name (_HID, "ACPI0010")
+  }
+
+  @param [in]  Generator              The SSDT Cpu Topology generator.
+  @param [in]  CfgMgrProtocol         Pointer to the Configuration Manager
+                                      Protocol Interface.
+  @param [in]  ParentNode             Parent node to attach the Cluster
+                                      node to.
+  @param [in]  ProcHierarchyNodeInfo  CM_ARM_PROC_HIERARCHY_INFO object used
+                                      to create the node.
+  @param [in]  ClusterIndex           Index used to generate the node name.
+  @param [out] ClusterNodePtr         If success, contains the created Cluster
+                                      node.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlCluster (
+  IN        ACPI_CPU_TOPOLOGY_GENERATOR           *       Generator,
+  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
+  IN        AML_NODE_HANDLE                               ParentNode,
+  IN        CM_ARM_PROC_HIERARCHY_INFO            *       ProcHierarchyNodeInfo,
+  IN        UINT32                                        ClusterIndex,
+  OUT       AML_OBJECT_NODE_HANDLE                *       ClusterNodePtr
+  )
+{
+  EFI_STATUS                Status;
+  AML_OBJECT_NODE_HANDLE    ClusterNode;
+  CHAR8                     AslNameCluster[AML_NAME_SEG_SIZE + 1];
+
+  ASSERT (Generator != NULL);
+  ASSERT (CfgMgrProtocol != NULL);
+  ASSERT (ParentNode != NULL);
+  ASSERT (ProcHierarchyNodeInfo != NULL);
+  ASSERT (ClusterNodePtr != NULL);
+
+  Status = WriteAslName ('C', ClusterIndex, AslNameCluster);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Status = AmlCodeGenDevice (AslNameCluster, ParentNode, &ClusterNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Use the ClusterIndex for the _UID value as there is no AcpiProcessorUid
+  // and EFI_ACPI_6_3_PPTT_PROCESSOR_ID_INVALID is set for non-Cpus.
+  Status = AmlCodeGenNameInteger (
+             "_UID",
+             ClusterIndex,
+             ClusterNode,
+             NULL
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Status = AmlCodeGenNameString (
+             "_HID",
+             ACPI_HID_PROCESSOR_CONTAINER_DEVICE,
+             ClusterNode,
+             NULL
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // If a set of Lpi states are associated with the
+  // CM_ARM_PROC_HIERARCHY_INFO, create an _LPI method returning them.
+  if (ProcHierarchyNodeInfo->LpiToken != CM_NULL_TOKEN) {
+    Status = CreateAmlLpiMethod (
+               Generator,
+               ProcHierarchyNodeInfo,
+               ClusterNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  }
+
+  *ClusterNodePtr = ClusterNode;
+
+  return Status;
+}
+
+/** Create an AML representation of the Cpu topology.
+
+  A cluster is by extension any non-leave device in the cpu topology.
+
+  @param [in] Generator          The SSDT Cpu Topology generator.
+  @param [in] CfgMgrProtocol     Pointer to the Configuration Manager
+                                 Protocol Interface.
+  @param [in] NodeToken          Token of the CM_ARM_PROC_HIERARCHY_INFO
+                                 currently handled.
+                                 Cannot be CM_NULL_TOKEN.
+  @param [in] ParentNode         Parent node to attach the created
+                                 node to.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateAmlCpuTopologyTree (
+  IN        ACPI_CPU_TOPOLOGY_GENERATOR           *       Generator,
+  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
+  IN        CM_OBJECT_TOKEN                               NodeToken,
+  IN        AML_NODE_HANDLE                               ParentNode
+  )
+{
+  EFI_STATUS              Status;
+  UINT32                  Index;
+  UINT32                  CpuIndex;
+  UINT32                  ClusterIndex;
+  AML_OBJECT_NODE_HANDLE  ClusterNode;
+
+  ASSERT (Generator != NULL);
+  ASSERT (Generator->ProcNodeList != NULL);
+  ASSERT (Generator->ProcNodeCount != 0);
+  ASSERT (CfgMgrProtocol != NULL);
+  ASSERT (NodeToken != CM_NULL_TOKEN);
+  ASSERT (ParentNode != NULL);
+
+  CpuIndex = 0;
+  ClusterIndex = 0;
+
+  for (Index = 0; Index < Generator->ProcNodeCount; Index++) {
+    // Find the children of the CM_ARM_PROC_HIERARCHY_INFO
+    // currently being handled (i.e. ParentToken == NodeToken).
+    if (Generator->ProcNodeList[Index].ParentToken == NodeToken) {
+
+      // Only Cpus (leaves in this tree) have a GicCToken.
+      // Create a Cpu node.
+      if (Generator->ProcNodeList[Index].GicCToken != CM_NULL_TOKEN) {
+        if ((Generator->ProcNodeList[Index].Flags & PPTT_PROCESSOR_MASK) !=
+             PPTT_CPU_PROCESSOR_MASK) {
+          DEBUG ((
+            DEBUG_ERROR,
+            "ERROR: SSDT-CPU-TOPOLOGY: Invalid flags for cpu: 0x%x.\n",
+            Generator->ProcNodeList[Index].Flags
+            ));
+          ASSERT (0);
+          return EFI_INVALID_PARAMETER;
+        }
+
+        Status = CreateAmlCpuFromProcHierarchy (
+                   Generator,
+                   CfgMgrProtocol,
+                   ParentNode,
+                   CpuIndex,
+                   &Generator->ProcNodeList[Index]
+                   );
+        if (EFI_ERROR (Status)) {
+          ASSERT (0);
+          return Status;
+        }
+
+        CpuIndex++;
+
+      } else {
+        // If this is not a Cpu, then this is a cluster.
+
+        // Acpi processor Id for clusters is not handled.
+        if ((Generator->ProcNodeList[Index].Flags & PPTT_PROCESSOR_MASK) !=
+             PPTT_CLUSTER_PROCESSOR_MASK) {
+          DEBUG ((
+            DEBUG_ERROR,
+            "ERROR: SSDT-CPU-TOPOLOGY: Invalid flags for cluster: 0x%x.\n",
+            Generator->ProcNodeList[Index].Flags
+            ));
+          ASSERT (0);
+          return EFI_INVALID_PARAMETER;
+        }
+
+        Status = CreateAmlCluster (
+                   Generator,
+                   CfgMgrProtocol,
+                   ParentNode,
+                   &Generator->ProcNodeList[Index],
+                   ClusterIndex,
+                   &ClusterNode
+                   );
+        if (EFI_ERROR (Status)) {
+          ASSERT (0);
+          return Status;
+        }
+
+        // Nodes must have a unique name in the ASL namespace.
+        // Reset the Cpu index whenever we create a new Cluster.
+        ClusterIndex++;
+        CpuIndex = 0;
+
+        // Recursively continue creating an AML tree.
+        Status = CreateAmlCpuTopologyTree (
+                   Generator,
+                   CfgMgrProtocol,
+                   Generator->ProcNodeList[Index].Token,
+                   ClusterNode
+                   );
+        if (EFI_ERROR (Status)) {
+          ASSERT (0);
+          return Status;
+        }
+      }
+    } // if ParentToken == NodeToken
+  } // for
+
+  return EFI_SUCCESS;
+}
+
+/** Create the processor hierarchy AML tree from CM_ARM_PROC_HIERARCHY_INFO
+    CM objects.
+
+  @param [in] Generator        The SSDT Cpu Topology generator.
+  @param [in] CfgMgrProtocol   Pointer to the Configuration Manager
+                               Protocol Interface.
+  @param [in] ScopeNode        Scope node handle ('\_SB' scope).
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateTopologyFromProcHierarchy (
+  IN        ACPI_CPU_TOPOLOGY_GENERATOR           *       Generator,
+  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
+  IN        AML_OBJECT_NODE_HANDLE                        ScopeNode
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Index;
+  UINT32      TopLevelProcNodeIndex;
+
+  ASSERT (Generator != NULL);
+  ASSERT (Generator->ProcNodeCount != 0);
+  ASSERT (Generator->ProcNodeList != NULL);
+  ASSERT (CfgMgrProtocol != NULL);
+  ASSERT (ScopeNode != NULL);
+
+  TopLevelProcNodeIndex = -1;
+
+  Status = TokenTableInitialize (Generator, Generator->ProcNodeCount);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // It is assumed that there is one unique CM_ARM_PROC_HIERARCHY_INFO
+  // structure with no ParentToken and the EFI_ACPI_6_3_PPTT_PACKAGE_PHYSICAL
+  // flag set. All other CM_ARM_PROC_HIERARCHY_INFO are non-physical and
+  // have a ParentToken.
+  for (Index = 0; Index < Generator->ProcNodeCount; Index++) {
+    if ((Generator->ProcNodeList[Index].ParentToken == CM_NULL_TOKEN) &&
+        (Generator->ProcNodeList[Index].Flags &
+          EFI_ACPI_6_3_PPTT_PACKAGE_PHYSICAL)) {
+      if (TopLevelProcNodeIndex != -1) {
+        DEBUG ((
+          DEBUG_ERROR,
+          "ERROR: SSDT-CPU-TOPOLOGY: Top level CM_ARM_PROC_HIERARCHY_INFO "
+          "must be unique\n"
+          ));
+        ASSERT (0);
+        goto exit_handler;
+      }
+      TopLevelProcNodeIndex = Index;
+    }
+  } // for
+
+  Status = CreateAmlCpuTopologyTree (
+             Generator,
+             CfgMgrProtocol,
+             Generator->ProcNodeList[TopLevelProcNodeIndex].Token,
+             ScopeNode
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  Status = GenerateLpiStates (Generator, CfgMgrProtocol, ScopeNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+exit_handler:
+  TokenTableFree (Generator);
+  return Status;
+}
+
+/** Create the processor hierarchy AML tree from CM_ARM_GICC_INFO
+    CM objects.
+
+  A cluster is by extension any non-leave device in the cpu topology.
+
+  @param [in] Generator        The SSDT Cpu Topology generator.
+  @param [in] CfgMgrProtocol   Pointer to the Configuration Manager
+                               Protocol Interface.
+  @param [in] ScopeNode        Scope node handle ('\_SB' scope).
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CreateTopologyFromGicC (
+  IN        ACPI_CPU_TOPOLOGY_GENERATOR           *       Generator,
+  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
+  IN        AML_OBJECT_NODE_HANDLE                        ScopeNode
+  )
+{
+  EFI_STATUS            Status;
+  CM_ARM_GICC_INFO    * GicCInfo;
+  UINT32                GicCInfoCount;
+  UINT32                Index;
+
+  ASSERT (Generator != NULL);
+  ASSERT (CfgMgrProtocol != NULL);
+  ASSERT (ScopeNode != NULL);
+
+  Status = GetEArmObjGicCInfo (
+             CfgMgrProtocol,
+             CM_NULL_TOKEN,
+             &GicCInfo,
+             &GicCInfoCount
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // For each CM_ARM_GICC_INFO object, create an AML node.
+  for (Index = 0; Index < GicCInfoCount; Index++) {
+    Status = CreateAmlCpu (
+               Generator,
+               ScopeNode,
+               &GicCInfo[Index],
+               Index,
+               NULL
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      break;
+    }
+  } // for
+
+  return Status;
+}
+
+/** Construct the SSDT Cpu Topology 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
+BuildSsdtCpuTopologyTable (
+  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;
+  AML_ROOT_NODE_HANDLE            RootNode;
+  AML_OBJECT_NODE_HANDLE          ScopeNode;
+  CM_ARM_PROC_HIERARCHY_INFO    * ProcHierarchyNodeList;
+  UINT32                          ProcHierarchyNodeCount;
+  ACPI_CPU_TOPOLOGY_GENERATOR   * Generator;
+
+  ASSERT (This != NULL);
+  ASSERT (AcpiTableInfo != NULL);
+  ASSERT (CfgMgrProtocol != NULL);
+  ASSERT (Table != NULL);
+  ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
+  ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
+
+  Generator = (ACPI_CPU_TOPOLOGY_GENERATOR*)This;
+
+  Status = AmlCodeGenDefinitionBlock (
+             "SSDT",
+             "ARMLTD",
+             "CPU-TOPO",
+             1,
+             &RootNode
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = AmlCodeGenScope (SB_SCOPE, RootNode, &ScopeNode);
+  if (EFI_ERROR (Status)) {
+    goto exit_handler;
+  }
+
+  // Get the processor hierarchy info and update the processor topology
+  // structure count with Processor Hierarchy Nodes (Type 0)
+  Status = GetEArmObjProcHierarchyInfo (
+             CfgMgrProtocol,
+             CM_NULL_TOKEN,
+             &ProcHierarchyNodeList,
+             &ProcHierarchyNodeCount
+             );
+  if (EFI_ERROR (Status) &&
+      (Status != EFI_NOT_FOUND)) {
+    goto exit_handler;
+  }
+
+  if (Status == EFI_NOT_FOUND) {
+    // If hierarchy information is not found generate a flat topology
+    // using CM_ARM_GICC_INFO objects.
+    Status = CreateTopologyFromGicC (
+               Generator,
+               CfgMgrProtocol,
+               ScopeNode
+               );
+    if (EFI_ERROR (Status)) {
+      goto exit_handler;
+    }
+  } else {
+    // Generate the topology from CM_ARM_PROC_HIERARCHY_INFO objects.
+    Generator->ProcNodeList = ProcHierarchyNodeList;
+    Generator->ProcNodeCount = ProcHierarchyNodeCount;
+
+    Status = CreateTopologyFromProcHierarchy (
+               Generator,
+               CfgMgrProtocol,
+               ScopeNode
+               );
+    if (EFI_ERROR (Status)) {
+      goto exit_handler;
+    }
+  }
+
+  Status = AmlSerializeDefinitionBlock (
+             RootNode,
+             Table
+             );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "ERROR: SSDT-CPU-TOPOLOGY: Failed to Serialize SSDT Table Data."
+      " Status = %r\n",
+      Status
+      ));
+    goto exit_handler;
+  }
+
+exit_handler:
+  // Delete the RootNode and its attached children.
+  return AmlDeleteTree (RootNode);
+}
+
+/** Free any resources allocated for constructing the
+    SSDT Cpu Topology ACPI table.
+
+  @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
+FreeSsdtCpuTopologyTableResources (
+  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: SSDT-CPU-TOPOLOGY: Invalid Table Pointer\n"));
+    ASSERT ((Table != NULL) && (*Table != NULL));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  FreePool (*Table);
+  *Table = NULL;
+  return EFI_SUCCESS;
+}
+
+/** This macro defines the SSDT Cpu Topology Table Generator revision.
+*/
+#define SSDT_CPU_TOPOLOGY_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the SSDT Cpu Topology Table Generator.
+*/
+STATIC
+ACPI_CPU_TOPOLOGY_GENERATOR SsdtCpuTopologyGenerator = {
+  // ACPI table generator header
+  {
+    // Generator ID
+    CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSsdtCpuTopology),
+    // Generator Description
+    L"ACPI.STD.SSDT.CPU.TOPOLOGY.GENERATOR",
+    // ACPI Table Signature
+    EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE,
+    // ACPI Table Revision - Unused
+    0,
+    // Minimum ACPI Table Revision - Unused
+    0,
+    // Creator ID
+    TABLE_GENERATOR_CREATOR_ID_ARM,
+    // Creator Revision
+    SSDT_CPU_TOPOLOGY_GENERATOR_REVISION,
+    // Build Table function
+    BuildSsdtCpuTopologyTable,
+    // Free Resource function
+    FreeSsdtCpuTopologyTableResources,
+    // 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.
+
+  // TokenTable
+  {
+      // Table
+      NULL,
+      // LastIndex
+      0
+  },
+  // ProcNodeList
+  NULL,
+  // ProcNodeCount
+  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
+AcpiSsdtCpuTopologyLibConstructor (
+  IN  EFI_HANDLE           ImageHandle,
+  IN  EFI_SYSTEM_TABLE  *  SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  Status = RegisterAcpiTableGenerator (&SsdtCpuTopologyGenerator.Header);
+  DEBUG ((
+    DEBUG_INFO,
+    "SSDT-CPU-TOPOLOGY: 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
+AcpiSsdtCpuTopologyLibDestructor (
+  IN  EFI_HANDLE           ImageHandle,
+  IN  EFI_SYSTEM_TABLE  *  SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  Status = DeregisterAcpiTableGenerator (&SsdtCpuTopologyGenerator.Header);
+  DEBUG ((
+    DEBUG_INFO,
+    "SSDT-CPU-TOPOLOGY: Deregister Generator. Status = %r\n",
+    Status
+    ));
+  ASSERT_EFI_ERROR (Status);
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.h b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.h
new file mode 100644
index 000000000000..95930a86b186
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.h
@@ -0,0 +1,134 @@
+/** @file
+  SSDT Cpu Topology Table Generator.
+
+  Copyright (c) 2021, Arm Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+    - ACPI 6.3 Specification - January 2019 - s8.4 Declaring Processors
+**/
+
+#ifndef SSDT_CPU_TOPOLOGY_GENERATOR_H_
+#define SSDT_CPU_TOPOLOGY_GENERATOR_H_
+
+#pragma pack(1)
+
+// Mask for the flags that need to be checked.
+#define PPTT_PROCESSOR_MASK   (                                               \
+          (EFI_ACPI_6_3_PPTT_PACKAGE_PHYSICAL)          |                     \
+          (EFI_ACPI_6_3_PPTT_PROCESSOR_ID_VALID << 1)   |                     \
+          (EFI_ACPI_6_3_PPTT_NODE_IS_LEAF << 3))
+
+// Mask for the cpu flags.
+#define PPTT_CPU_PROCESSOR_MASK   (                                           \
+          (EFI_ACPI_6_3_PPTT_PACKAGE_NOT_PHYSICAL)      |                     \
+          (EFI_ACPI_6_3_PPTT_PROCESSOR_ID_VALID << 1)   |                     \
+          (EFI_ACPI_6_3_PPTT_NODE_IS_LEAF << 3))
+
+// Mask for the cluster flags.
+// Even though a _UID is generated for clusters, it is simpler to use
+// EFI_ACPI_6_3_PPTT_PROCESSOR_ID_INVALID and to not match the cluster id of
+// the PPTT table (not sure the PPTT table is generated).
+#define PPTT_CLUSTER_PROCESSOR_MASK   (                                       \
+          (EFI_ACPI_6_3_PPTT_PACKAGE_NOT_PHYSICAL)      |                     \
+          (EFI_ACPI_6_3_PPTT_PROCESSOR_ID_INVALID << 1) |                     \
+          (EFI_ACPI_6_3_PPTT_NODE_IS_NOT_LEAF << 3))
+
+/** LPI states are stored in the ASL namespace at '\_SB_.Lxxx',
+    with xxx being the node index of the LPI state.
+*/
+#define SB_SCOPE                            "\\_SB_"
+#define SB_SCOPE_PREFIX                     SB_SCOPE "."
+/// Size of the SB_SCOPE_PREFIX string.
+#define SB_SCOPE_PREFIX_SIZE                sizeof (SB_SCOPE_PREFIX)
+
+/// HID for a processor device.
+#define ACPI_HID_PROCESSOR_DEVICE           "ACPI0007"
+
+/// HID for a processor container device.
+#define ACPI_HID_PROCESSOR_CONTAINER_DEVICE "ACPI0010"
+
+/** Node names of Cpus and Clusters are 'Cxxx', and 'Lxxx' for LPI states.
+    The 'xxx' is an index on 12 bits is given to node name,
+    thus the limitation in the number of nodes.
+*/
+#define MAX_INDEX_NAME                      (1 << 12)
+
+/** A structure used to handle the Lpi structures referencing.
+
+  A CM_ARM_PROC_HIERARCHY_INFO structure references a CM_ARM_OBJ_REF.
+  This CM_ARM_OBJ_REF references CM_ARM_LPI_INFO structures.
+
+  Example:
+  (Cpu0)                                   (Cpu1)
+  CM_ARM_PROC_HIERARCHY_INFO               CM_ARM_PROC_HIERARCHY_INFO
+              |                                       |
+              +----------------------------------------
+              |
+              v
+  (List of references to Lpi states)
+  CM_ARM_OBJ_REF
+              |
+              +----------------------------------------
+              |                                       |
+              v                                       v
+  (A first Lpi state)                       (A second Lpi state)
+  CM_ARM_LPI_INFO[0]                        CM_ARM_LPI_INFO[1]
+
+  Here, Cpu0 and Cpu1 have the same Lpi states. Both CM_ARM_PROC_HIERARCHY_INFO
+  structures reference the same CM_ARM_OBJ_REF. An entry is created in the
+  TokenTable such as:
+  0 <-> CM_ARM_OBJ_REF
+
+  This will lead to the creation of this pseudo-ASL code where Cpu0 and Cpu1
+  return the same object at \_SB.L000:
+  Scope (\_SB) {
+    Device (C000) {
+      [...]
+      Method (_LPI) {
+        Return (\_SB.L000)
+      }
+    } // C000
+
+    Device (C001) {
+      [...]
+      Method (_LPI) {
+        Return (\_SB.L000)
+      }
+    } // C001
+
+    // Lpi states
+    Name (L000, Package (0x05) {
+      [...]
+    }
+  }
+*/
+typedef struct TokenTable {
+  /// TokenTable, a table allowing to map:
+  /// Index <-> CM_OBJECT_TOKEN (to CM_ARM_LPI_INFO structures).
+  CM_OBJECT_TOKEN             * Table;
+
+  /// Last used index of the TokenTable.
+  /// LastIndex is bound by ProcNodeCount.
+  UINT32                        LastIndex;
+} TOKEN_TABLE;
+
+/** A structure holding the Cpu topology generator and additional private data.
+*/
+typedef struct AcpiCpuTopologyGenerator {
+  /// ACPI Table generator header
+  ACPI_TABLE_GENERATOR          Header;
+
+  // Private fields are defined from here.
+
+  /// Private object used to handle token referencing.
+  TOKEN_TABLE                   TokenTable;
+  /// List of CM_ARM_PROC_HIERARCHY_INFO CM objects.
+  CM_ARM_PROC_HIERARCHY_INFO  * ProcNodeList;
+  /// Count of CM_ARM_PROC_HIERARCHY_INFO CM objects.
+  UINT32                        ProcNodeCount;
+} ACPI_CPU_TOPOLOGY_GENERATOR;
+
+#pragma pack()
+
+#endif // SSDT_CPU_TOPOLOGY_GENERATOR_H_
diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf
new file mode 100644
index 000000000000..4038499d963d
--- /dev/null
+++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf
@@ -0,0 +1,40 @@
+## @file
+# Ssdt Cpu Topology Table Generator
+#
+#  Copyright (c) 2021, Arm Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION    = 0x0001001B
+  BASE_NAME      = SsdtCpuTopologyLibArm
+  FILE_GUID      = F2835EB6-4B05-48D4-A475-147DA0F3755C
+  VERSION_STRING = 1.0
+  MODULE_TYPE    = DXE_DRIVER
+  LIBRARY_CLASS  = NULL|DXE_DRIVER
+  CONSTRUCTOR    = AcpiSsdtCpuTopologyLibConstructor
+  DESTRUCTOR     = AcpiSsdtCpuTopologyLibDestructor
+
+[Sources]
+  SsdtCpuTopologyGenerator.c
+  SsdtCpuTopologyGenerator.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  ArmPlatformPkg/ArmPlatformPkg.dec
+  DynamicTablesPkg/DynamicTablesPkg.dec
+
+[LibraryClasses]
+  AcpiHelperLib
+  AmlLib
+  BaseLib
+
+[FixedPcd]
+
+[Protocols]
+
+[Guids]
+
-- 
2.17.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#76955): https://edk2.groups.io/g/devel/message/76955
Mute This Topic: https://groups.io/mt/83735666/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-


Re: [edk2-devel] [PATCH v1 13/13] DynamicTablesPkg: SSDT CPU topology and LPI state generator
Posted by Sami Mujawar 4 years, 4 months ago
Hi Pierre,

I ahve a few minor suggestions marked inline as [SAMI].

With those changed.

Reviewed-by: Sami Mujawar <sami.mujawar@arm.com>

Regards,

Sami Mujawar


On 23/06/2021 12:40 PM, Pierre.Gondois@arm.com wrote:
> From: Pierre Gondois <Pierre.Gondois@arm.com>
>
> In the GIC interrupt model, logical processors are required to
> have a Processor Device object in the DSDT and must convey each
> processor's GIC information to the OS using the GICC structure.
> Additionally, _LPI objects may be needed as they provide a method
> to describe Low Power Idle states that defines the local power
> states for each node in a hierarchical processor topology.
>
> Therefore, add support to generate the CPU topology and the LPI
> state information in an SSDT table.
>
> Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
> ---
>   DynamicTablesPkg/DynamicTables.dsc.inc        |    6 +
>   DynamicTablesPkg/Include/AcpiTableGenerator.h |    7 +-
>   .../SsdtCpuTopologyGenerator.c                | 1230 +++++++++++++++++
>   .../SsdtCpuTopologyGenerator.h                |  134 ++
>   .../SsdtCpuTopologyLibArm.inf                 |   40 +
>   5 files changed, 1416 insertions(+), 1 deletion(-)
>   create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.c
>   create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.h
>   create mode 100644 DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf
>
> diff --git a/DynamicTablesPkg/DynamicTables.dsc.inc b/DynamicTablesPkg/DynamicTables.dsc.inc
> index ed221d1681eb..292215c39456 100644
> --- a/DynamicTablesPkg/DynamicTables.dsc.inc
> +++ b/DynamicTablesPkg/DynamicTables.dsc.inc
> @@ -37,6 +37,9 @@ [Components.common]
>     DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf
>     DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf
>   
> +  # AML Codegen
> +  DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf
> +
>     #
>     # Dynamic Table Factory Dxe
>     #
> @@ -56,6 +59,9 @@ [Components.common]
>         # AML Fixup
>         NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtSerialPortLibArm/SsdtSerialPortLibArm.inf
>         NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCmn600LibArm/SsdtCmn600LibArm.inf
> +
> +      # AML Codegen
> +      NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf
>     }
>   
>     #
> diff --git a/DynamicTablesPkg/Include/AcpiTableGenerator.h b/DynamicTablesPkg/Include/AcpiTableGenerator.h
> index 352331d6dc95..45c808ba740d 100644
> --- a/DynamicTablesPkg/Include/AcpiTableGenerator.h
> +++ b/DynamicTablesPkg/Include/AcpiTableGenerator.h
> @@ -1,6 +1,6 @@
>   /** @file
>   
> -  Copyright (c) 2017 - 2020, Arm Limited. All rights reserved.<BR>
> +  Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR>
>   
>     SPDX-License-Identifier: BSD-2-Clause-Patent
>   
> @@ -63,6 +63,10 @@ The Dynamic Tables Framework implements the following ACPI table generators:
>               The SSDT CMN-600 generator collates the CMN-600 information
>               from the Configuration Manager and patches the SSDT CMN-600
>               template to build the SSDT CMN-600 table.
> +  - SSDT Cpu-Topology:
> +            The SSDT Cpu-Topology generator collates the cpu and LPI
> +            information from the Configuration Manager and generates a
> +            SSDT table describing the CPU hierarchy.
>   */
>   
>   /** The ACPI_TABLE_GENERATOR_ID type describes ACPI table generator ID.
> @@ -88,6 +92,7 @@ typedef enum StdAcpiTableId {
>     EStdAcpiTableIdSrat,                          ///< SRAT Generator
>     EStdAcpiTableIdSsdtSerialPort,                ///< SSDT Serial-Port Generator
>     EStdAcpiTableIdSsdtCmn600,                    ///< SSDT Cmn-600 Generator
> +  EStdAcpiTableIdSsdtCpuTopology,               ///< SSDT Cpu Topology
>     EStdAcpiTableIdMax
>   } ESTD_ACPI_TABLE_ID;
>   
> diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.c b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.c
> new file mode 100644
> index 000000000000..88db808760f7
> --- /dev/null
> +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.c
> @@ -0,0 +1,1230 @@
> +/** @file
> +  SSDT Cpu Topology Table Generator.
> +
> +  Copyright (c) 2021, Arm Limited. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Reference(s):
> +    - ACPI 6.3 Specification - January 2019 - s8.4 Declaring Processors
> +**/
> +
> +#include <IndustryStandard/DebugPort2Table.h>
[SAMI] I think the DBG2 header is not required here. Can you check this 
and if any other includes below can be removed, please?
> +#include <Library/AcpiLib.h>
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Protocol/AcpiTable.h>
> +
> +// Module specific include files.
> +#include <AcpiTableGenerator.h>
> +#include <ConfigurationManagerObject.h>
> +#include <ConfigurationManagerHelper.h>
> +#include <Library/AcpiHelperLib.h>
> +#include <Library/AmlLib/AmlLib.h>
> +#include <Protocol/ConfigurationManagerProtocol.h>
> +
> +#include "SsdtCpuTopologyGenerator.h"
> +
> +/** ARM standard SSDT Cpu Topology Table Generator.
> +
> +Requirements:
> +  The following Configuration Manager Object(s) are required by
> +  this Generator:
> +  - EArmObjProcHierarchyInfo
> +  - EArmObjGicCInfo
> +  - EArmObjCmRef
> +  - EArmObjLpiInfo
[SAMI] I think the LPI information should be marked as (OPTIONAL).
> +*/
> +
> +/** This macro expands to a function that retrieves the GIC
> +    CPU interface Information from the Configuration Manager.
> +*/
> +GET_OBJECT_LIST (
> +  EObjNameSpaceArm,
> +  EArmObjGicCInfo,
> +  CM_ARM_GICC_INFO
> +  );
> +
> +/**
> +  This macro expands to a function that retrieves the Processor Hierarchy
> +  information from the Configuration Manager.
> +*/
> +GET_OBJECT_LIST (
> +  EObjNameSpaceArm,
> +  EArmObjProcHierarchyInfo,
> +  CM_ARM_PROC_HIERARCHY_INFO
> +  );
> +
> +/**
> +  This macro expands to a function that retrieves the cross-CM-object-
> +  reference information from the Configuration Manager.
> +*/
> +GET_OBJECT_LIST (
> +  EObjNameSpaceArm,
> +  EArmObjCmRef,
> +  CM_ARM_OBJ_REF
> +  );
> +
> +/**
> +  This macro expands to a function that retrieves the Lpi
> +  information from the Configuration Manager.
> +*/
> +GET_OBJECT_LIST (
> +  EObjNameSpaceArm,
> +  EArmObjLpiInfo,
> +  CM_ARM_LPI_INFO
> +  );
> +
> +/** Initialize the TokenTable.
> +
> +  One entry should be allocated for each CM_ARM_PROC_HIERARCHY_INFO
> +  structure of the platform. The TokenTable allows to have a mapping:
> +  Index <-> CM_OBJECT_TOKEN (to CM_ARM_LPI_INFO structures).
> +
> +  There will always be less sets of Lpi states (CM_ARM_OBJ_REF)
> +  than the number of cpus/clusters (CM_ARM_PROC_HIERARCHY_INFO).
> +
> +  @param [in]  Generator  The SSDT Cpu Topology generator.
> +  @param [in]  Count      Number of entries to allocate in the TokenTable.
> +
> +  @retval EFI_SUCCESS            Success.
> +  @retval EFI_INVALID_PARAMETER  Invalid parameter.
> +  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +TokenTableInitialize (
> +  IN  ACPI_CPU_TOPOLOGY_GENERATOR   * Generator,
> +  IN  UINT32                          Count
> +  )
> +{
> +  CM_OBJECT_TOKEN   * Table;
> +
> +  if ((Generator == NULL) ||
> +      (Count == 0)        ||
> +      (Count >= MAX_INDEX_NAME)) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Table = AllocateZeroPool (sizeof (CM_OBJECT_TOKEN) * Count);
> +  if (Table == NULL) {
> +    ASSERT (0);
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  Generator->TokenTable.Table = Table;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/** Free the TokenTable.
> +
> +  @param [in]  Generator    The SSDT Cpu Topology generator.
> +**/
> +STATIC
> +VOID
> +EFIAPI
> +TokenTableFree (
> +  IN  ACPI_CPU_TOPOLOGY_GENERATOR   * Generator
> +  )
> +{
> +  ASSERT (Generator != NULL);
> +  ASSERT (Generator->TokenTable.Table != NULL);
> +
> +  if (Generator->TokenTable.Table != NULL) {
> +    FreePool (Generator->TokenTable.Table);
> +  }
> +}
> +
> +/** Add a new entry to the TokenTable and return its index.
> +
> +  If an entry with Token is already available in the table,
> +  return its index without adding a new entry.
> +
> +  @param [in]  Generator  The SSDT Cpu Topology generator.
> +  @param [in]  Token      New Token entry to add.
> +
> +  @retval The index of the token entry in the TokenTable.
> +**/
> +STATIC
> +UINT32
> +EFIAPI
> +TokenTableAdd (
> +  IN  ACPI_CPU_TOPOLOGY_GENERATOR   * Generator,
> +  IN  CM_OBJECT_TOKEN                 Token
> +  )
> +{
> +  CM_OBJECT_TOKEN   * Table;
> +  UINT32              Index;
> +  UINT32              LastIndex;
> +
> +  ASSERT (Generator != NULL);
> +  ASSERT (Generator->TokenTable.Table != NULL);
> +
> +  Table = Generator->TokenTable.Table;
> +  LastIndex = Generator->TokenTable.LastIndex;
> +
> +  // Search if there is already an entry with this Token.
> +  for (Index = 0; Index < LastIndex; Index++) {
> +    if (Table[Index] == Token) {
> +      return Index;
> +    }
> +  }
> +
> +  ASSERT (LastIndex < MAX_INDEX_NAME);
> +  ASSERT (LastIndex < Generator->ProcNodeCount);
> +
> +  // If no, create a new entry.
> +  Table[LastIndex] = Token;
> +
> +  return Generator->TokenTable.LastIndex++;
> +}
> +
> +/** Write a string 'Xxxx\0' in AslName (5 bytes long),
> +  with 'X' being the leading char of the name, and
> +  with 'xxx' being Value in hexadecimal.
> +
> +  As 'xxx' in hexadecimal represents a number on 12 bits,
> +  we have Value < (2 << 12)
> +
> +  @param [in]       LeadChar  Leading char of the name.
> +  @param [in]       Value     Hex value of the name.
> +                              Must be lower than (2 << 12).
> +  @param [in, out]  AslName   Pointer to write the 'Xxxx' string to.
> +                              Must be at least 5 bytes long.
> +
> +  @retval EFI_SUCCESS               Success.
> +  @retval EFI_INVALID_PARAMETER     Invalid parameter.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +WriteAslName (
> +  IN      CHAR8     LeadChar,
> +  IN      UINT32    Value,
> +  IN OUT  CHAR8   * AslName
> +  )
> +{
> +  UINT8   Index;
> +
> +  if ((Value >= MAX_INDEX_NAME)  ||
> +      (AslName == NULL)) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  AslName[0] = LeadChar;
> +  AslName[AML_NAME_SEG_SIZE] = '\0';
> +
> +  for (Index = 0; Index < AML_NAME_SEG_SIZE - 1; Index++) {
> +    AslName[AML_NAME_SEG_SIZE - Index - 1] =
> +      AsciiFromHex (((Value >> (4 * Index)) & 0xF));
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/** Create and add an _LPI method to Cpu/Cluster Node.
> +
> +  For instance, transform an AML node from:
> +  Device (C002)
> +  {
> +      Name (_UID, 2)
> +      Name (_HID, "ACPI0007")
> +  }
> +
> +  To:
> +  Device (C002)
> +  {
> +      Name (_UID, 2)
> +      Name (_HID, "ACPI0007")
> +      Method (_LPI, 0, NotSerialized)
> +      {
> +          Return (\_SB.L003)
> +      }
> +  }
> +
> +  @param [in]  Generator              The SSDT Cpu Topology generator.
> +  @param [in]  ProcHierarchyNodeInfo  CM_ARM_PROC_HIERARCHY_INFO describing
> +                                      the Cpu.
> +  @param [in]  Node                   Node to which the _LPI method is
> +                                      attached. Can represent a Cpu or a
> +                                      Cluster.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +CreateAmlLpiMethod (
> +  IN  ACPI_CPU_TOPOLOGY_GENERATOR   * Generator,
> +  IN  CM_ARM_PROC_HIERARCHY_INFO    * ProcHierarchyNodeInfo,
> +  IN  AML_OBJECT_NODE_HANDLE        * Node
> +  )
> +{
> +  EFI_STATUS    Status;
> +  UINT32        TokenIndex;
> +  CHAR8         AslName[SB_SCOPE_PREFIX_SIZE + AML_NAME_SEG_SIZE];
> +
> +  ASSERT (Generator != NULL);
> +  ASSERT (ProcHierarchyNodeInfo != NULL);
> +  ASSERT (ProcHierarchyNodeInfo->LpiToken != CM_NULL_TOKEN);
> +  ASSERT (Node != NULL);
> +
> +  TokenIndex = TokenTableAdd (Generator, ProcHierarchyNodeInfo->LpiToken);
> +
> +  CopyMem (AslName, SB_SCOPE_PREFIX, SB_SCOPE_PREFIX_SIZE);
> +
> +  Status = WriteAslName (
> +             'L',
> +             TokenIndex,
> +             AslName + SB_SCOPE_PREFIX_SIZE - 1
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  // ASL:
> +  // Method (_LPI, 0) {
> +  //   Return ([AslName])
> +  // }
> +  Status = AmlCodeGenMethodRetNameString (
> +             "_LPI",
> +             AslName,
> +             0,
> +             FALSE,
> +             0,
> +             Node,
> +             NULL
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +  }
> +
> +  return Status;
> +}
> +
> +/** Generate all the Lpi states under the '_SB' scope.
> +
> +  This function generates the following ASL code:
> +  Scope (\_SB) {
> +    Name (L000, Package() {
> +      0, // Version
> +      0, // Level Index
> +      X, // Count
> +      Package() {
> +        [An Lpi state]
> +      },
> +      Package() {
> +        [Another Lpi state]
> +      },
> +    } // Name L000
> +
> +    Name (L001, Package() {
> +      ...
> +    } // Name L001
> +
> +    ...
> +  } // Scope /_SB
> +
> +  The Lpi states are fetched from the Configuration Manager.
> +  The names of the Lpi states are generated from the TokenTable.
> +
> +  @param [in]  Generator        The SSDT Cpu Topology generator.
> +  @param [in]  CfgMgrProtocol   Pointer to the Configuration Manager
> +                                Protocol Interface.
> +  @param [in] ScopeNode         Scope node handle ('\_SB' scope).
> +
> +  @retval EFI_SUCCESS             Success.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +GenerateLpiStates (
> +  IN        ACPI_CPU_TOPOLOGY_GENERATOR           *       Generator,
> +  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
> +  IN        AML_OBJECT_NODE_HANDLE                        ScopeNode
> +  )
> +{
> +  EFI_STATUS                Status;
> +
> +  UINT32                    Index;
> +  UINT32                    LastIndex;
> +
> +  AML_OBJECT_NODE_HANDLE    LpiNode;
> +  CM_ARM_OBJ_REF          * LpiRefInfo;
> +  UINT32                    LpiRefInfoCount;
> +  UINT32                    LpiRefIndex;
> +  CM_ARM_LPI_INFO         * LpiInfo;
> +  CHAR8                     AslName[AML_NAME_SEG_SIZE + 1];
> +
> +  ASSERT (Generator != NULL);
> +  ASSERT (Generator->TokenTable.Table != NULL);
> +  ASSERT (CfgMgrProtocol != NULL);
> +  ASSERT (ScopeNode != NULL);
> +
> +  LastIndex = Generator->TokenTable.LastIndex;
> +
> +  // For each entry in the TokenTable, create a name in the AML namespace
> +  // under SB_SCOPE, to store the Lpi states associated with the LpiToken.
> +  for (Index = 0; Index < LastIndex; Index++) {
> +    Status = WriteAslName ('L', Index, AslName);
> +    if (EFI_ERROR (Status)) {
> +      ASSERT (0);
> +      return Status;
> +    }
> +
> +    // We do not support the LevelId field for now, let it to 0.
> +    Status = AmlCreateLpiNode (AslName, 1, 0, ScopeNode, &LpiNode);
> +    if (EFI_ERROR (Status)) {
> +      ASSERT (0);
> +      return Status;
> +    }
> +
> +    // Fetch the LPI objects referenced by the token.
> +    Status = GetEArmObjCmRef (
> +               CfgMgrProtocol,
> +               Generator->TokenTable.Table[Index],
> +               &LpiRefInfo,
> +               &LpiRefInfoCount
> +               );
> +    if (EFI_ERROR (Status)) {
> +      ASSERT (0);
> +      return Status;
> +    }
> +
> +    for (LpiRefIndex = 0; LpiRefIndex < LpiRefInfoCount; LpiRefIndex++) {
> +      // For each CM_ARM_LPI_INFO referenced by the token, add an Lpi state.
> +      Status = GetEArmObjLpiInfo (
> +                 CfgMgrProtocol,
> +                 LpiRefInfo[LpiRefIndex].ReferenceToken,
> +                 &LpiInfo,
> +                 NULL
> +                 );
> +      if (EFI_ERROR (Status)) {
> +        ASSERT (0);
> +        return Status;
> +      }
> +
> +      Status = AmlAddLpiState (
> +                 LpiInfo->MinResidency,
> +                 LpiInfo->WorstCaseWakeLatency,
> +                 LpiInfo->Flags,
> +                 LpiInfo->ArchFlags,
> +                 LpiInfo->ResCntFreq,
> +                 LpiInfo->EnableParentState,
> +                 LpiInfo->IsInteger ?
> +                   NULL :
> +                   &LpiInfo->RegisterEntryMethod,
> +                 LpiInfo->IsInteger ?
> +                   LpiInfo->IntegerEntryMethod :
> +                   0,
> +                 &LpiInfo->ResidencyCounterRegister,
> +                 &LpiInfo->UsageCounterRegister,
> +                 LpiInfo->StateName,
> +                 LpiNode
> +                 );
> +      if (EFI_ERROR (Status)) {
> +        ASSERT (0);
> +        return Status;
> +      }
> +    } // for LpiRefIndex
> +  } // for Index
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/** Create a Cpu in the AML namespace.
> +
> +  This generates the following ASL code:
> +  Device (C002)
> +  {
> +      Name (_UID, 2)
> +      Name (_HID, "ACPI0007")
> +  }
> +
> +  @param [in]  Generator    The SSDT Cpu Topology generator.
> +  @param [in]  ParentNode   Parent node to attach the Cpu node to.
> +  @param [in]  GicCInfo     CM_ARM_GICC_INFO object used to create the node.
> +  @param [in]  CpuIndex     Index used to generate the node name.
> +  @param [out] CpuNodePtr   If not NULL, return the created Cpu node.
> +
> +  @retval EFI_SUCCESS             Success.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +CreateAmlCpu (
> +  IN   ACPI_CPU_TOPOLOGY_GENERATOR   * Generator,
> +  IN   AML_NODE_HANDLE                 ParentNode,
> +  IN   CM_ARM_GICC_INFO              * GicCInfo,
> +  IN   UINT32                          CpuIndex,
> +  OUT  AML_OBJECT_NODE_HANDLE        * CpuNodePtr OPTIONAL
> +  )
> +{
> +  EFI_STATUS                Status;
> +  AML_OBJECT_NODE_HANDLE    CpuNode;
> +  CHAR8                     AslName[AML_NAME_SEG_SIZE + 1];
> +
> +  ASSERT (Generator != NULL);
> +  ASSERT (ParentNode != NULL);
> +  ASSERT (GicCInfo != NULL);
> +
> +  Status = WriteAslName ('C', CpuIndex, AslName);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  Status = AmlCodeGenDevice (AslName, ParentNode, &CpuNode);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  Status = AmlCodeGenNameInteger (
> +             "_UID",
> +             GicCInfo->AcpiProcessorUid,
> +             CpuNode,
> +             NULL
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  Status = AmlCodeGenNameString (
> +             "_HID",
> +             ACPI_HID_PROCESSOR_DEVICE,
> +             CpuNode,
> +             NULL
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  // If requested, return the handle to the CpuNode.
> +  if (CpuNodePtr != NULL) {
> +    *CpuNodePtr = CpuNode;
> +  }
> +
> +  return Status;
> +}
> +
> +/** Create a Cpu in the AML namespace from a CM_ARM_PROC_HIERARCHY_INFO
> +    CM object.
> +
> +  @param [in]  Generator              The SSDT Cpu Topology generator.
> +  @param [in]  CfgMgrProtocol         Pointer to the Configuration Manager
> +                                      Protocol Interface.
> +  @param [in]  ParentNode             Parent node to attach the Cpu node to.
> +  @param [in]  CpuIndex               Index used to generate the node name.
> +  @param [in]  ProcHierarchyNodeInfo  CM_ARM_PROC_HIERARCHY_INFO describing
> +                                      the Cpu.
> +
> +  @retval EFI_SUCCESS             Success.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +CreateAmlCpuFromProcHierarchy (
> +  IN        ACPI_CPU_TOPOLOGY_GENERATOR           *       Generator,
> +  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
> +  IN        AML_NODE_HANDLE                               ParentNode,
> +  IN        UINT32                                        CpuIndex,
> +  IN        CM_ARM_PROC_HIERARCHY_INFO            *       ProcHierarchyNodeInfo
> +  )
> +{
> +  EFI_STATUS                Status;
> +  CM_ARM_GICC_INFO        * GicCInfo;
> +  AML_OBJECT_NODE_HANDLE    CpuNode;
> +
> +  ASSERT (Generator != NULL);
> +  ASSERT (CfgMgrProtocol != NULL);
> +  ASSERT (ParentNode != NULL);
> +  ASSERT (ProcHierarchyNodeInfo != NULL);
> +  ASSERT (ProcHierarchyNodeInfo->GicCToken != CM_NULL_TOKEN);
> +
> +  Status = GetEArmObjGicCInfo (
> +             CfgMgrProtocol,
> +             ProcHierarchyNodeInfo->GicCToken,
> +             &GicCInfo,
> +             NULL
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  Status = CreateAmlCpu (Generator, ParentNode, GicCInfo, CpuIndex, &CpuNode);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  // If a set of Lpi states is associated with the
> +  // CM_ARM_PROC_HIERARCHY_INFO, create an _LPI method returning them.
> +  if (ProcHierarchyNodeInfo->LpiToken != CM_NULL_TOKEN) {
> +    Status = CreateAmlLpiMethod (Generator, ProcHierarchyNodeInfo, CpuNode);
> +    ASSERT_EFI_ERROR (Status);
> +  }
> +
> +  return Status;
> +}
> +
> +/** Create a Cluster in the AML namespace.
> +
> +  Any CM_ARM_PROC_HIERARCHY_INFO object with the following flags is
> +  assumed to be a cluster:
> +   - EFI_ACPI_6_3_PPTT_PACKAGE_NOT_PHYSICAL
> +   - EFI_ACPI_6_3_PPTT_PROCESSOR_ID_INVALID
> +   - EFI_ACPI_6_3_PPTT_NODE_IS_NOT_LEAF
> +
> +  This generates the following ASL code:
> +  Device (C002)
> +  {
> +      Name (_UID, 2)
> +      Name (_HID, "ACPI0010")
> +  }
> +
> +  @param [in]  Generator              The SSDT Cpu Topology generator.
> +  @param [in]  CfgMgrProtocol         Pointer to the Configuration Manager
> +                                      Protocol Interface.
> +  @param [in]  ParentNode             Parent node to attach the Cluster
> +                                      node to.
> +  @param [in]  ProcHierarchyNodeInfo  CM_ARM_PROC_HIERARCHY_INFO object used
> +                                      to create the node.
> +  @param [in]  ClusterIndex           Index used to generate the node name.
> +  @param [out] ClusterNodePtr         If success, contains the created Cluster
> +                                      node.
> +
> +  @retval EFI_SUCCESS             Success.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +CreateAmlCluster (
> +  IN        ACPI_CPU_TOPOLOGY_GENERATOR           *       Generator,
> +  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
> +  IN        AML_NODE_HANDLE                               ParentNode,
> +  IN        CM_ARM_PROC_HIERARCHY_INFO            *       ProcHierarchyNodeInfo,
> +  IN        UINT32                                        ClusterIndex,
> +  OUT       AML_OBJECT_NODE_HANDLE                *       ClusterNodePtr
> +  )
> +{
> +  EFI_STATUS                Status;
> +  AML_OBJECT_NODE_HANDLE    ClusterNode;
> +  CHAR8                     AslNameCluster[AML_NAME_SEG_SIZE + 1];
> +
> +  ASSERT (Generator != NULL);
> +  ASSERT (CfgMgrProtocol != NULL);
> +  ASSERT (ParentNode != NULL);
> +  ASSERT (ProcHierarchyNodeInfo != NULL);
> +  ASSERT (ClusterNodePtr != NULL);
> +
> +  Status = WriteAslName ('C', ClusterIndex, AslNameCluster);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  Status = AmlCodeGenDevice (AslNameCluster, ParentNode, &ClusterNode);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  // Use the ClusterIndex for the _UID value as there is no AcpiProcessorUid
> +  // and EFI_ACPI_6_3_PPTT_PROCESSOR_ID_INVALID is set for non-Cpus.
> +  Status = AmlCodeGenNameInteger (
> +             "_UID",
> +             ClusterIndex,
> +             ClusterNode,
> +             NULL
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  Status = AmlCodeGenNameString (
> +             "_HID",
> +             ACPI_HID_PROCESSOR_CONTAINER_DEVICE,
> +             ClusterNode,
> +             NULL
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  // If a set of Lpi states are associated with the
> +  // CM_ARM_PROC_HIERARCHY_INFO, create an _LPI method returning them.
> +  if (ProcHierarchyNodeInfo->LpiToken != CM_NULL_TOKEN) {
> +    Status = CreateAmlLpiMethod (
> +               Generator,
> +               ProcHierarchyNodeInfo,
> +               ClusterNode
> +               );
> +    if (EFI_ERROR (Status)) {
> +      ASSERT (0);
> +      return Status;
> +    }
> +  }
> +
> +  *ClusterNodePtr = ClusterNode;
> +
> +  return Status;
> +}
> +
> +/** Create an AML representation of the Cpu topology.
> +
> +  A cluster is by extension any non-leave device in the cpu topology.
> +
> +  @param [in] Generator          The SSDT Cpu Topology generator.
> +  @param [in] CfgMgrProtocol     Pointer to the Configuration Manager
> +                                 Protocol Interface.
> +  @param [in] NodeToken          Token of the CM_ARM_PROC_HIERARCHY_INFO
> +                                 currently handled.
> +                                 Cannot be CM_NULL_TOKEN.
> +  @param [in] ParentNode         Parent node to attach the created
> +                                 node to.
> +
> +  @retval EFI_SUCCESS             Success.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +CreateAmlCpuTopologyTree (
> +  IN        ACPI_CPU_TOPOLOGY_GENERATOR           *       Generator,
> +  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
> +  IN        CM_OBJECT_TOKEN                               NodeToken,
> +  IN        AML_NODE_HANDLE                               ParentNode
> +  )
> +{
> +  EFI_STATUS              Status;
> +  UINT32                  Index;
> +  UINT32                  CpuIndex;
> +  UINT32                  ClusterIndex;
> +  AML_OBJECT_NODE_HANDLE  ClusterNode;
> +
> +  ASSERT (Generator != NULL);
> +  ASSERT (Generator->ProcNodeList != NULL);
> +  ASSERT (Generator->ProcNodeCount != 0);
> +  ASSERT (CfgMgrProtocol != NULL);
> +  ASSERT (NodeToken != CM_NULL_TOKEN);
> +  ASSERT (ParentNode != NULL);
> +
> +  CpuIndex = 0;
> +  ClusterIndex = 0;
> +
> +  for (Index = 0; Index < Generator->ProcNodeCount; Index++) {
> +    // Find the children of the CM_ARM_PROC_HIERARCHY_INFO
> +    // currently being handled (i.e. ParentToken == NodeToken).
> +    if (Generator->ProcNodeList[Index].ParentToken == NodeToken) {
> +
> +      // Only Cpus (leaves in this tree) have a GicCToken.
> +      // Create a Cpu node.
> +      if (Generator->ProcNodeList[Index].GicCToken != CM_NULL_TOKEN) {
> +        if ((Generator->ProcNodeList[Index].Flags & PPTT_PROCESSOR_MASK) !=
> +             PPTT_CPU_PROCESSOR_MASK) {
> +          DEBUG ((
> +            DEBUG_ERROR,
> +            "ERROR: SSDT-CPU-TOPOLOGY: Invalid flags for cpu: 0x%x.\n",
> +            Generator->ProcNodeList[Index].Flags
> +            ));
> +          ASSERT (0);
> +          return EFI_INVALID_PARAMETER;
> +        }
> +
> +        Status = CreateAmlCpuFromProcHierarchy (
> +                   Generator,
> +                   CfgMgrProtocol,
> +                   ParentNode,
> +                   CpuIndex,
> +                   &Generator->ProcNodeList[Index]
> +                   );
> +        if (EFI_ERROR (Status)) {
> +          ASSERT (0);
> +          return Status;
> +        }
> +
> +        CpuIndex++;
> +
> +      } else {
> +        // If this is not a Cpu, then this is a cluster.
> +
> +        // Acpi processor Id for clusters is not handled.
> +        if ((Generator->ProcNodeList[Index].Flags & PPTT_PROCESSOR_MASK) !=
> +             PPTT_CLUSTER_PROCESSOR_MASK) {
> +          DEBUG ((
> +            DEBUG_ERROR,
> +            "ERROR: SSDT-CPU-TOPOLOGY: Invalid flags for cluster: 0x%x.\n",
> +            Generator->ProcNodeList[Index].Flags
> +            ));
> +          ASSERT (0);
> +          return EFI_INVALID_PARAMETER;
> +        }
> +
> +        Status = CreateAmlCluster (
> +                   Generator,
> +                   CfgMgrProtocol,
> +                   ParentNode,
> +                   &Generator->ProcNodeList[Index],
> +                   ClusterIndex,
> +                   &ClusterNode
> +                   );
> +        if (EFI_ERROR (Status)) {
> +          ASSERT (0);
> +          return Status;
> +        }
> +
> +        // Nodes must have a unique name in the ASL namespace.
> +        // Reset the Cpu index whenever we create a new Cluster.
> +        ClusterIndex++;
> +        CpuIndex = 0;
> +
> +        // Recursively continue creating an AML tree.
> +        Status = CreateAmlCpuTopologyTree (
> +                   Generator,
> +                   CfgMgrProtocol,
> +                   Generator->ProcNodeList[Index].Token,
> +                   ClusterNode
> +                   );
> +        if (EFI_ERROR (Status)) {
> +          ASSERT (0);
> +          return Status;
> +        }
> +      }
> +    } // if ParentToken == NodeToken
> +  } // for
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/** Create the processor hierarchy AML tree from CM_ARM_PROC_HIERARCHY_INFO
> +    CM objects.
> +
> +  @param [in] Generator        The SSDT Cpu Topology generator.
> +  @param [in] CfgMgrProtocol   Pointer to the Configuration Manager
> +                               Protocol Interface.
> +  @param [in] ScopeNode        Scope node handle ('\_SB' scope).
> +
> +  @retval EFI_SUCCESS             Success.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +CreateTopologyFromProcHierarchy (
> +  IN        ACPI_CPU_TOPOLOGY_GENERATOR           *       Generator,
> +  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
> +  IN        AML_OBJECT_NODE_HANDLE                        ScopeNode
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINT32      Index;
> +  UINT32      TopLevelProcNodeIndex;
> +
> +  ASSERT (Generator != NULL);
> +  ASSERT (Generator->ProcNodeCount != 0);
> +  ASSERT (Generator->ProcNodeList != NULL);
> +  ASSERT (CfgMgrProtocol != NULL);
> +  ASSERT (ScopeNode != NULL);
> +
> +  TopLevelProcNodeIndex = -1;
[SAMI] I think it would be good to use MAX_UINT32 instead of -1 here. 
Same for the if condition a few lines below.
> +
> +  Status = TokenTableInitialize (Generator, Generator->ProcNodeCount);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  // It is assumed that there is one unique CM_ARM_PROC_HIERARCHY_INFO
> +  // structure with no ParentToken and the EFI_ACPI_6_3_PPTT_PACKAGE_PHYSICAL
> +  // flag set. All other CM_ARM_PROC_HIERARCHY_INFO are non-physical and
> +  // have a ParentToken.
> +  for (Index = 0; Index < Generator->ProcNodeCount; Index++) {
> +    if ((Generator->ProcNodeList[Index].ParentToken == CM_NULL_TOKEN) &&
> +        (Generator->ProcNodeList[Index].Flags &
> +          EFI_ACPI_6_3_PPTT_PACKAGE_PHYSICAL)) {
> +      if (TopLevelProcNodeIndex != -1) {
> +        DEBUG ((
> +          DEBUG_ERROR,
> +          "ERROR: SSDT-CPU-TOPOLOGY: Top level CM_ARM_PROC_HIERARCHY_INFO "
> +          "must be unique\n"
> +          ));
> +        ASSERT (0);
> +        goto exit_handler;
> +      }
> +      TopLevelProcNodeIndex = Index;
> +    }
> +  } // for
> +
> +  Status = CreateAmlCpuTopologyTree (
> +             Generator,
> +             CfgMgrProtocol,
> +             Generator->ProcNodeList[TopLevelProcNodeIndex].Token,
> +             ScopeNode
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    goto exit_handler;
> +  }
> +
> +  Status = GenerateLpiStates (Generator, CfgMgrProtocol, ScopeNode);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    goto exit_handler;
> +  }
> +
> +exit_handler:
> +  TokenTableFree (Generator);
> +  return Status;
> +}
> +
> +/** Create the processor hierarchy AML tree from CM_ARM_GICC_INFO
> +    CM objects.
> +
> +  A cluster is by extension any non-leave device in the cpu topology.
> +
> +  @param [in] Generator        The SSDT Cpu Topology generator.
> +  @param [in] CfgMgrProtocol   Pointer to the Configuration Manager
> +                               Protocol Interface.
> +  @param [in] ScopeNode        Scope node handle ('\_SB' scope).
> +
> +  @retval EFI_SUCCESS             Success.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +CreateTopologyFromGicC (
> +  IN        ACPI_CPU_TOPOLOGY_GENERATOR           *       Generator,
> +  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
> +  IN        AML_OBJECT_NODE_HANDLE                        ScopeNode
> +  )
> +{
> +  EFI_STATUS            Status;
> +  CM_ARM_GICC_INFO    * GicCInfo;
> +  UINT32                GicCInfoCount;
> +  UINT32                Index;
> +
> +  ASSERT (Generator != NULL);
> +  ASSERT (CfgMgrProtocol != NULL);
> +  ASSERT (ScopeNode != NULL);
> +
> +  Status = GetEArmObjGicCInfo (
> +             CfgMgrProtocol,
> +             CM_NULL_TOKEN,
> +             &GicCInfo,
> +             &GicCInfoCount
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  // For each CM_ARM_GICC_INFO object, create an AML node.
> +  for (Index = 0; Index < GicCInfoCount; Index++) {
> +    Status = CreateAmlCpu (
> +               Generator,
> +               ScopeNode,
> +               &GicCInfo[Index],
> +               Index,
> +               NULL
> +               );
> +    if (EFI_ERROR (Status)) {
> +      ASSERT (0);
> +      break;
> +    }
> +  } // for
> +
> +  return Status;
> +}
> +
> +/** Construct the SSDT Cpu Topology 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
> +BuildSsdtCpuTopologyTable (
> +  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;
> +  AML_ROOT_NODE_HANDLE            RootNode;
> +  AML_OBJECT_NODE_HANDLE          ScopeNode;
> +  CM_ARM_PROC_HIERARCHY_INFO    * ProcHierarchyNodeList;
> +  UINT32                          ProcHierarchyNodeCount;
> +  ACPI_CPU_TOPOLOGY_GENERATOR   * Generator;
> +
> +  ASSERT (This != NULL);
> +  ASSERT (AcpiTableInfo != NULL);
> +  ASSERT (CfgMgrProtocol != NULL);
> +  ASSERT (Table != NULL);
> +  ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
> +  ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
> +
> +  Generator = (ACPI_CPU_TOPOLOGY_GENERATOR*)This;
> +
> +  Status = AmlCodeGenDefinitionBlock (
> +             "SSDT",
> +             "ARMLTD",
> +             "CPU-TOPO",
> +             1,
> +             &RootNode
> +             );
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  Status = AmlCodeGenScope (SB_SCOPE, RootNode, &ScopeNode);
> +  if (EFI_ERROR (Status)) {
> +    goto exit_handler;
> +  }
> +
> +  // Get the processor hierarchy info and update the processor topology
> +  // structure count with Processor Hierarchy Nodes (Type 0)
> +  Status = GetEArmObjProcHierarchyInfo (
> +             CfgMgrProtocol,
> +             CM_NULL_TOKEN,
> +             &ProcHierarchyNodeList,
> +             &ProcHierarchyNodeCount
> +             );
> +  if (EFI_ERROR (Status) &&
> +      (Status != EFI_NOT_FOUND)) {
> +    goto exit_handler;
> +  }
> +
> +  if (Status == EFI_NOT_FOUND) {
> +    // If hierarchy information is not found generate a flat topology
> +    // using CM_ARM_GICC_INFO objects.
> +    Status = CreateTopologyFromGicC (
> +               Generator,
> +               CfgMgrProtocol,
> +               ScopeNode
> +               );
> +    if (EFI_ERROR (Status)) {
> +      goto exit_handler;
> +    }
> +  } else {
> +    // Generate the topology from CM_ARM_PROC_HIERARCHY_INFO objects.
> +    Generator->ProcNodeList = ProcHierarchyNodeList;
> +    Generator->ProcNodeCount = ProcHierarchyNodeCount;
> +
> +    Status = CreateTopologyFromProcHierarchy (
> +               Generator,
> +               CfgMgrProtocol,
> +               ScopeNode
> +               );
> +    if (EFI_ERROR (Status)) {
> +      goto exit_handler;
> +    }
> +  }
> +
> +  Status = AmlSerializeDefinitionBlock (
> +             RootNode,
> +             Table
> +             );
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((
> +      DEBUG_ERROR,
> +      "ERROR: SSDT-CPU-TOPOLOGY: Failed to Serialize SSDT Table Data."
> +      " Status = %r\n",
> +      Status
> +      ));
> +    goto exit_handler;
> +  }
> +
> +exit_handler:
> +  // Delete the RootNode and its attached children.
> +  return AmlDeleteTree (RootNode);
> +}
> +
> +/** Free any resources allocated for constructing the
> +    SSDT Cpu Topology ACPI table.
> +
> +  @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
> +FreeSsdtCpuTopologyTableResources (
> +  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: SSDT-CPU-TOPOLOGY: Invalid Table Pointer\n"));
> +    ASSERT ((Table != NULL) && (*Table != NULL));
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  FreePool (*Table);
> +  *Table = NULL;
> +  return EFI_SUCCESS;
> +}
> +
> +/** This macro defines the SSDT Cpu Topology Table Generator revision.
> +*/
> +#define SSDT_CPU_TOPOLOGY_GENERATOR_REVISION CREATE_REVISION (1, 0)
> +
> +/** The interface for the SSDT Cpu Topology Table Generator.
> +*/
> +STATIC
> +ACPI_CPU_TOPOLOGY_GENERATOR SsdtCpuTopologyGenerator = {
> +  // ACPI table generator header
> +  {
> +    // Generator ID
> +    CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSsdtCpuTopology),
> +    // Generator Description
> +    L"ACPI.STD.SSDT.CPU.TOPOLOGY.GENERATOR",
> +    // ACPI Table Signature
> +    EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE,
> +    // ACPI Table Revision - Unused
> +    0,
> +    // Minimum ACPI Table Revision - Unused
> +    0,
> +    // Creator ID
> +    TABLE_GENERATOR_CREATOR_ID_ARM,
> +    // Creator Revision
> +    SSDT_CPU_TOPOLOGY_GENERATOR_REVISION,
> +    // Build Table function
> +    BuildSsdtCpuTopologyTable,
> +    // Free Resource function
> +    FreeSsdtCpuTopologyTableResources,
> +    // 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.
> +
> +  // TokenTable
> +  {
> +      // Table
> +      NULL,
> +      // LastIndex
> +      0
> +  },
> +  // ProcNodeList
> +  NULL,
> +  // ProcNodeCount
> +  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
> +AcpiSsdtCpuTopologyLibConstructor (
> +  IN  EFI_HANDLE           ImageHandle,
> +  IN  EFI_SYSTEM_TABLE  *  SystemTable
> +  )
> +{
> +  EFI_STATUS  Status;
> +  Status = RegisterAcpiTableGenerator (&SsdtCpuTopologyGenerator.Header);
> +  DEBUG ((
> +    DEBUG_INFO,
> +    "SSDT-CPU-TOPOLOGY: 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
> +AcpiSsdtCpuTopologyLibDestructor (
> +  IN  EFI_HANDLE           ImageHandle,
> +  IN  EFI_SYSTEM_TABLE  *  SystemTable
> +  )
> +{
> +  EFI_STATUS  Status;
> +  Status = DeregisterAcpiTableGenerator (&SsdtCpuTopologyGenerator.Header);
> +  DEBUG ((
> +    DEBUG_INFO,
> +    "SSDT-CPU-TOPOLOGY: Deregister Generator. Status = %r\n",
> +    Status
> +    ));
> +  ASSERT_EFI_ERROR (Status);
> +  return Status;
> +}
> diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.h b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.h
> new file mode 100644
> index 000000000000..95930a86b186
> --- /dev/null
> +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyGenerator.h
> @@ -0,0 +1,134 @@
> +/** @file
> +  SSDT Cpu Topology Table Generator.
> +
> +  Copyright (c) 2021, Arm Limited. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Reference(s):
> +    - ACPI 6.3 Specification - January 2019 - s8.4 Declaring Processors
> +**/
> +
> +#ifndef SSDT_CPU_TOPOLOGY_GENERATOR_H_
> +#define SSDT_CPU_TOPOLOGY_GENERATOR_H_
> +
> +#pragma pack(1)
> +
> +// Mask for the flags that need to be checked.
> +#define PPTT_PROCESSOR_MASK   (                                               \
> +          (EFI_ACPI_6_3_PPTT_PACKAGE_PHYSICAL)          |                     \
> +          (EFI_ACPI_6_3_PPTT_PROCESSOR_ID_VALID << 1)   |                     \
> +          (EFI_ACPI_6_3_PPTT_NODE_IS_LEAF << 3))
> +
> +// Mask for the cpu flags.
> +#define PPTT_CPU_PROCESSOR_MASK   (                                           \
> +          (EFI_ACPI_6_3_PPTT_PACKAGE_NOT_PHYSICAL)      |                     \
> +          (EFI_ACPI_6_3_PPTT_PROCESSOR_ID_VALID << 1)   |                     \
> +          (EFI_ACPI_6_3_PPTT_NODE_IS_LEAF << 3))
> +
> +// Mask for the cluster flags.
> +// Even though a _UID is generated for clusters, it is simpler to use
> +// EFI_ACPI_6_3_PPTT_PROCESSOR_ID_INVALID and to not match the cluster id of
> +// the PPTT table (not sure the PPTT table is generated).
> +#define PPTT_CLUSTER_PROCESSOR_MASK   (                                       \
> +          (EFI_ACPI_6_3_PPTT_PACKAGE_NOT_PHYSICAL)      |                     \
> +          (EFI_ACPI_6_3_PPTT_PROCESSOR_ID_INVALID << 1) |                     \
> +          (EFI_ACPI_6_3_PPTT_NODE_IS_NOT_LEAF << 3))
> +
> +/** LPI states are stored in the ASL namespace at '\_SB_.Lxxx',
> +    with xxx being the node index of the LPI state.
> +*/
> +#define SB_SCOPE                            "\\_SB_"
> +#define SB_SCOPE_PREFIX                     SB_SCOPE "."
> +/// Size of the SB_SCOPE_PREFIX string.
> +#define SB_SCOPE_PREFIX_SIZE                sizeof (SB_SCOPE_PREFIX)
> +
> +/// HID for a processor device.
> +#define ACPI_HID_PROCESSOR_DEVICE           "ACPI0007"
> +
> +/// HID for a processor container device.
> +#define ACPI_HID_PROCESSOR_CONTAINER_DEVICE "ACPI0010"
> +
> +/** Node names of Cpus and Clusters are 'Cxxx', and 'Lxxx' for LPI states.
> +    The 'xxx' is an index on 12 bits is given to node name,
> +    thus the limitation in the number of nodes.
> +*/
> +#define MAX_INDEX_NAME                      (1 << 12)
[SAMI] I think this macro should be renamed to MAX_NODE_COUNT.
> +
> +/** A structure used to handle the Lpi structures referencing.
> +
> +  A CM_ARM_PROC_HIERARCHY_INFO structure references a CM_ARM_OBJ_REF.
> +  This CM_ARM_OBJ_REF references CM_ARM_LPI_INFO structures.
> +
> +  Example:
> +  (Cpu0)                                   (Cpu1)
> +  CM_ARM_PROC_HIERARCHY_INFO               CM_ARM_PROC_HIERARCHY_INFO
> +              |                                       |
> +              +----------------------------------------
> +              |
> +              v
> +  (List of references to Lpi states)
> +  CM_ARM_OBJ_REF
> +              |
> +              +----------------------------------------
> +              |                                       |
> +              v                                       v
> +  (A first Lpi state)                       (A second Lpi state)
> +  CM_ARM_LPI_INFO[0]                        CM_ARM_LPI_INFO[1]
> +
> +  Here, Cpu0 and Cpu1 have the same Lpi states. Both CM_ARM_PROC_HIERARCHY_INFO
> +  structures reference the same CM_ARM_OBJ_REF. An entry is created in the
> +  TokenTable such as:
> +  0 <-> CM_ARM_OBJ_REF
> +
> +  This will lead to the creation of this pseudo-ASL code where Cpu0 and Cpu1
> +  return the same object at \_SB.L000:
> +  Scope (\_SB) {
> +    Device (C000) {
> +      [...]
> +      Method (_LPI) {
> +        Return (\_SB.L000)
> +      }
> +    } // C000
> +
> +    Device (C001) {
> +      [...]
> +      Method (_LPI) {
> +        Return (\_SB.L000)
> +      }
> +    } // C001
> +
> +    // Lpi states
> +    Name (L000, Package (0x05) {
> +      [...]
> +    }
> +  }
> +*/
> +typedef struct TokenTable {
> +  /// TokenTable, a table allowing to map:
> +  /// Index <-> CM_OBJECT_TOKEN (to CM_ARM_LPI_INFO structures).
> +  CM_OBJECT_TOKEN             * Table;
> +
> +  /// Last used index of the TokenTable.
> +  /// LastIndex is bound by ProcNodeCount.
> +  UINT32                        LastIndex;
> +} TOKEN_TABLE;
> +
> +/** A structure holding the Cpu topology generator and additional private data.
> +*/
> +typedef struct AcpiCpuTopologyGenerator {
> +  /// ACPI Table generator header
> +  ACPI_TABLE_GENERATOR          Header;
> +
> +  // Private fields are defined from here.
> +
> +  /// Private object used to handle token referencing.
> +  TOKEN_TABLE                   TokenTable;
> +  /// List of CM_ARM_PROC_HIERARCHY_INFO CM objects.
> +  CM_ARM_PROC_HIERARCHY_INFO  * ProcNodeList;
> +  /// Count of CM_ARM_PROC_HIERARCHY_INFO CM objects.
> +  UINT32                        ProcNodeCount;
> +} ACPI_CPU_TOPOLOGY_GENERATOR;
> +
> +#pragma pack()
> +
> +#endif // SSDT_CPU_TOPOLOGY_GENERATOR_H_
> diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf
> new file mode 100644
> index 000000000000..4038499d963d
> --- /dev/null
> +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtCpuTopologyLibArm/SsdtCpuTopologyLibArm.inf
> @@ -0,0 +1,40 @@
> +## @file
> +# Ssdt Cpu Topology Table Generator
> +#
> +#  Copyright (c) 2021, Arm Limited. All rights reserved.<BR>
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +##
> +
> +[Defines]
> +  INF_VERSION    = 0x0001001B
> +  BASE_NAME      = SsdtCpuTopologyLibArm
> +  FILE_GUID      = F2835EB6-4B05-48D4-A475-147DA0F3755C
> +  VERSION_STRING = 1.0
> +  MODULE_TYPE    = DXE_DRIVER
> +  LIBRARY_CLASS  = NULL|DXE_DRIVER
> +  CONSTRUCTOR    = AcpiSsdtCpuTopologyLibConstructor
> +  DESTRUCTOR     = AcpiSsdtCpuTopologyLibDestructor
> +
> +[Sources]
> +  SsdtCpuTopologyGenerator.c
> +  SsdtCpuTopologyGenerator.h
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  EmbeddedPkg/EmbeddedPkg.dec
> +  ArmPlatformPkg/ArmPlatformPkg.dec
> +  DynamicTablesPkg/DynamicTablesPkg.dec
> +
> +[LibraryClasses]
> +  AcpiHelperLib
> +  AmlLib
> +  BaseLib
> +
> +[FixedPcd]
> +
> +[Protocols]
> +
> +[Guids]
[SAMI] Please remove unused sections.
> +



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#81511): https://edk2.groups.io/g/devel/message/81511
Mute This Topic: https://groups.io/mt/83735666/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-