summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c1540
1 files changed, 1540 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c
new file mode 100644
index 00000000..13c932dd
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c
@@ -0,0 +1,1540 @@
+/** @file
+ PPTT Table Generator
+
+ Copyright (c) 2019, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - ACPI 6.3 Specification, January 2019
+
+ @par Glossary:
+ - Cm or CM - Configuration Manager
+ - Obj or OBJ - Object
+**/
+
+#include <Library/AcpiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Protocol/AcpiTable.h>
+
+// Module specific include files.
+#include <AcpiTableGenerator.h>
+#include <ConfigurationManagerObject.h>
+#include <ConfigurationManagerHelper.h>
+#include <Library/TableHelperLib.h>
+#include <Protocol/ConfigurationManagerProtocol.h>
+
+#include "PpttGenerator.h"
+
+/**
+ ARM standard PPTT Generator
+
+ Requirements:
+ The following Configuration Manager Object(s) are used by this Generator:
+ - EArmObjProcHierarchyInfo (REQUIRED)
+ - EArmObjCacheInfo
+ - EArmObjProcNodeIdInfo
+ - EArmObjCmRef
+ - EArmObjGicCInfo (REQUIRED)
+*/
+
+/**
+ 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 cache information
+ from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjCacheInfo,
+ CM_ARM_CACHE_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the ID information for
+ Processor Hierarchy Nodes from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjProcNodeIdInfo,
+ CM_ARM_PROC_NODE_ID_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 GIC CPU interface
+ information from the Configuration Manager.
+*/
+GET_OBJECT_LIST (
+ EObjNameSpaceArm,
+ EArmObjGicCInfo,
+ CM_ARM_GICC_INFO
+ );
+
+/**
+ Returns the size of the PPTT Processor Hierarchy Node (Type 0) given a
+ Processor Hierarchy Info CM object.
+
+ @param [in] Node Pointer to Processor Hierarchy Info CM object which
+ represents the Processor Hierarchy Node to be generated.
+
+ @retval Size of the Processor Hierarchy Node in bytes.
+**/
+STATIC
+UINT32
+GetProcHierarchyNodeSize (
+ IN CONST CM_ARM_PROC_HIERARCHY_INFO * Node
+ )
+{
+ ASSERT (Node != NULL);
+
+ // <size of Processor Hierarchy Node> + <size of Private Resources array>
+ return sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR) +
+ (Node->NoOfPrivateResources * sizeof (UINT32));
+}
+
+/**
+ This macro expands to a function that retrieves the amount of memory required
+ to store the Processor Hierarchy Nodes (Type 0) and updates the Node Indexer.
+*/
+GET_SIZE_OF_PPTT_STRUCTS (
+ ProcHierarchyNodes,
+ GetProcHierarchyNodeSize (NodesToIndex),
+ CM_ARM_PROC_HIERARCHY_INFO
+ );
+
+/**
+ This macro expands to a function that retrieves the amount of memory required
+ to store the Cache Type Structures (Type 1) and updates the Node Indexer.
+*/
+GET_SIZE_OF_PPTT_STRUCTS (
+ CacheTypeStructs,
+ sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE),
+ CM_ARM_CACHE_INFO
+ );
+
+/** This macro expands to a function that retrieves the amount of memory
+ required to store the ID Structures (Type 2) and updates the Node Indexer.
+*/
+GET_SIZE_OF_PPTT_STRUCTS (
+ IdStructs,
+ sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_ID),
+ CM_ARM_PROC_NODE_ID_INFO
+ );
+
+/**
+ Search the Node Indexer and return the indexed PPTT node with the given
+ Token.
+
+ @param [in] NodeIndexer Pointer to the Node Indexer array.
+ @param [in] NodeCount Number of elements in Node Indexer.
+ @param [in] SearchToken Token used for Node Indexer lookup.
+ @param [out] IndexedNodeFound Pointer to the Node Indexer array element
+ with the given Token.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_NOT_FOUND No element with a matching token was
+ found in the Node Indexer array.
+**/
+STATIC
+EFI_STATUS
+GetPpttNodeReferencedByToken (
+ IN PPTT_NODE_INDEXER * NodeIndexer,
+ IN UINT32 NodeCount,
+ IN CONST CM_OBJECT_TOKEN SearchToken,
+ OUT PPTT_NODE_INDEXER ** IndexedNodeFound
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (NodeIndexer != NULL);
+
+ DEBUG ((
+ DEBUG_INFO,
+ "PPTT: Node Indexer: SearchToken = %p\n",
+ SearchToken
+ ));
+
+ while (NodeCount-- != 0) {
+ DEBUG ((
+ DEBUG_INFO,
+ "PPTT: Node Indexer: NodeIndexer->Token = %p. Offset = %d\n",
+ NodeIndexer->Token,
+ NodeIndexer->Offset
+ ));
+
+ if (NodeIndexer->Token == SearchToken) {
+ *IndexedNodeFound = NodeIndexer;
+ Status = EFI_SUCCESS;
+ DEBUG ((
+ DEBUG_INFO,
+ "PPTT: Node Indexer: Token = %p. Found, Status = %r\n",
+ SearchToken,
+ Status
+ ));
+ return Status;
+ }
+ NodeIndexer++;
+ }
+
+ Status = EFI_NOT_FOUND;
+ DEBUG ((
+ DEBUG_ERROR,
+ "PPTT: Node Indexer: SearchToken = %p. Status = %r\n",
+ SearchToken,
+ Status
+ ));
+
+ return Status;
+}
+
+/**
+ Detect cycles in the processor and cache topology graph represented in
+ the PPTT table.
+
+ @param [in] Generator Pointer to the PPTT Generator.
+
+ @retval EFI_SUCCESS There are no cyclic references in the graph.
+ @retval EFI_INVALID_PARAMETER Processor or cache references form a cycle.
+**/
+STATIC
+EFI_STATUS
+DetectCyclesInTopology (
+ IN CONST ACPI_PPTT_GENERATOR * CONST Generator
+ )
+{
+ EFI_STATUS Status;
+ PPTT_NODE_INDEXER * Iterator;
+ PPTT_NODE_INDEXER * CycleDetector;
+ UINT32 NodesRemaining;
+
+ ASSERT (Generator != NULL);
+
+ Iterator = Generator->NodeIndexer;
+ NodesRemaining = Generator->ProcTopologyStructCount;
+
+ while (NodesRemaining != 0) {
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: PPTT: Cycle detection for element with index %d\n",
+ Generator->ProcTopologyStructCount - NodesRemaining
+ ));
+
+ CycleDetector = Iterator;
+
+ // Walk the topology tree
+ while (CycleDetector->TopologyParent != NULL) {
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: PPTT: %p -> %p\n",
+ CycleDetector->Token,
+ CycleDetector->TopologyParent->Token
+ ));
+
+ // Check if we have already visited this node
+ if (CycleDetector->CycleDetectionStamp == NodesRemaining) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Cycle in processor and cache topology detected for " \
+ "a chain of references originating from a node with: Token = %p " \
+ "Status = %r\n",
+ Iterator->Token,
+ Status
+ ));
+ return Status;
+ }
+
+ // Stamp the visited node
+ CycleDetector->CycleDetectionStamp = NodesRemaining;
+ CycleDetector = CycleDetector->TopologyParent;
+ } // Continue topology tree walk
+
+ Iterator++;
+ NodesRemaining--;
+ } // Next Node Indexer
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Update the array of private resources for a given Processor Hierarchy Node.
+
+ @param [in] Generator Pointer to the PPTT Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] PrivResArray Pointer to the array of private resources.
+ @param [in] PrivResCount Number of private resources.
+ @param [in] PrivResArrayToken Reference Token for the CM_ARM_OBJ_REF
+ array describing node's private resources.
+
+ @retval EFI_SUCCESS Array updated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND A private resource was not found.
+**/
+STATIC
+EFI_STATUS
+AddPrivateResources (
+ IN CONST ACPI_PPTT_GENERATOR * CONST Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN UINT32 * PrivResArray,
+ IN UINT32 PrivResCount,
+ IN CONST CM_OBJECT_TOKEN PrivResArrayToken
+ )
+{
+ EFI_STATUS Status;
+ CM_ARM_OBJ_REF * CmObjRefs;
+ UINT32 CmObjRefCount;
+ PPTT_NODE_INDEXER * PpttNodeFound;
+
+ ASSERT (
+ (Generator != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (PrivResArray != NULL) &&
+ (PrivResCount != 0)
+ );
+
+ // Validate input arguments
+ if (PrivResArrayToken == CM_NULL_TOKEN) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The number of private resources is %d while " \
+ "PrivResToken = CM_NULL_TOKEN. Status = %r\n",
+ PrivResCount,
+ Status
+ ));
+ return Status;
+ }
+
+ CmObjRefCount = 0;
+ // Get the CM Object References
+ Status = GetEArmObjCmRef (
+ CfgMgrProtocol,
+ PrivResArrayToken,
+ &CmObjRefs,
+ &CmObjRefCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get CM Object References. " \
+ "PrivResToken = %p. Status = %r\n",
+ PrivResArrayToken,
+ Status
+ ));
+ return Status;
+ }
+
+ if (CmObjRefCount != PrivResCount) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The number of CM Object References retrieved and the " \
+ "number of private resources don't match. CmObjRefCount = %d. " \
+ "PrivResourceCount = %d. PrivResToken = %p. Status = %r\n",
+ CmObjRefCount,
+ PrivResCount,
+ PrivResArrayToken,
+ Status
+ ));
+ return Status;
+ }
+
+ while (PrivResCount-- != 0) {
+ if (CmObjRefs->ReferenceToken == CM_NULL_TOKEN) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: CM_NULL_TOKEN provided as reference token for a " \
+ "private resource. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ // The Node indexer has the Processor hierarchy nodes at the begining
+ // followed by the cache structs and Id structs. Therefore we can
+ // skip the Processor hierarchy nodes in the node indexer search.
+ Status = GetPpttNodeReferencedByToken (
+ Generator->CacheStructIndexedList,
+ (Generator->ProcTopologyStructCount -
+ Generator->ProcHierarchyNodeCount),
+ CmObjRefs->ReferenceToken,
+ &PpttNodeFound
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get a private resource with Token = %p from " \
+ "Node Indexer. Status = %r\n",
+ CmObjRefs->ReferenceToken,
+ Status
+ ));
+ return Status;
+ }
+
+ // Update the offset of the private resources in the Processor
+ // Hierarchy Node structure
+ *(PrivResArray++) = PpttNodeFound->Offset;
+ CmObjRefs++;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Function to test if two indexed Processor Hierarchy Info objects map to the
+ same GIC CPU Interface Info object.
+
+ This is a callback function that can be invoked by FindDuplicateValue ().
+
+ @param [in] Object1 Pointer to the first indexed Processor Hierarchy
+ Info object.
+ @param [in] Object2 Pointer to the second indexed Processor Hierarchy
+ Info object.
+ @param [in] Index1 Index of Object1 to be displayed for debugging
+ purposes.
+ @param [in] Index2 Index of Object2 to be displayed for debugging
+ purposes.
+
+ @retval TRUE Object1 and Object2 have the same GicCToken.
+ @retval FALSE Object1 and Object2 have different GicCTokens.
+**/
+BOOLEAN
+EFIAPI
+IsGicCTokenEqual (
+ IN CONST VOID * Object1,
+ IN CONST VOID * Object2,
+ IN UINTN Index1,
+ IN UINTN Index2
+ )
+{
+ PPTT_NODE_INDEXER * IndexedObject1;
+ PPTT_NODE_INDEXER * IndexedObject2;
+ CM_ARM_PROC_HIERARCHY_INFO * ProcNode1;
+ CM_ARM_PROC_HIERARCHY_INFO * ProcNode2;
+
+ ASSERT (
+ (Object1 != NULL) &&
+ (Object2 != NULL)
+ );
+
+ IndexedObject1 = (PPTT_NODE_INDEXER*)Object1;
+ IndexedObject2 = (PPTT_NODE_INDEXER*)Object2;
+ ProcNode1 = (CM_ARM_PROC_HIERARCHY_INFO*)IndexedObject1->Object;
+ ProcNode2 = (CM_ARM_PROC_HIERARCHY_INFO*)IndexedObject2->Object;
+
+ if (IS_ACPI_PROC_ID_VALID (ProcNode1) &&
+ IS_ACPI_PROC_ID_VALID (ProcNode2) &&
+ (ProcNode1->GicCToken != CM_NULL_TOKEN) &&
+ (ProcNode2->GicCToken != CM_NULL_TOKEN) &&
+ (ProcNode1->GicCToken == ProcNode2->GicCToken)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Two Processor Hierarchy Info objects (%d and %d) map to " \
+ "the same GICC Info object. ACPI Processor IDs are not unique. " \
+ "GicCToken = %p.\n",
+ Index1,
+ IndexedObject1->Token,
+ Index2,
+ ProcNode1->GicCToken
+ ));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Update the Processor Hierarchy Node (Type 0) information.
+
+ This function populates the Processor Hierarchy Nodes with information from
+ the Configuration Manager and adds this information to the PPTT table.
+
+ @param [in] Generator Pointer to the PPTT Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Pptt Pointer to PPTT table structure.
+ @param [in] NodesStartOffset Offset from the start of PPTT table to the
+ start of Processor Hierarchy Nodes.
+
+ @retval EFI_SUCCESS Node updated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddProcHierarchyNodes (
+ IN CONST ACPI_PPTT_GENERATOR * CONST Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt,
+ IN CONST UINT32 NodesStartOffset
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR * ProcStruct;
+ UINT32 * PrivateResources;
+ BOOLEAN IsGicCTokenDuplicated;
+
+ CM_ARM_GICC_INFO * GicCInfoList;
+ UINT32 GicCInfoCount;
+ UINT32 UniqueGicCRefCount;
+
+ PPTT_NODE_INDEXER * PpttNodeFound;
+ CM_ARM_PROC_HIERARCHY_INFO * ProcInfoNode;
+
+ PPTT_NODE_INDEXER * ProcNodeIterator;
+ UINT32 NodeCount;
+ UINT32 Length;
+
+ ASSERT (
+ (Generator != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (Pptt != NULL)
+ );
+
+ ProcStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR*)((UINT8*)Pptt +
+ NodesStartOffset);
+
+ ProcNodeIterator = Generator->ProcHierarchyNodeIndexedList;
+ NodeCount = Generator->ProcHierarchyNodeCount;
+
+ // Check if every GICC Object is referenced by onlu one Proc Node
+ IsGicCTokenDuplicated = FindDuplicateValue (
+ ProcNodeIterator,
+ NodeCount,
+ sizeof (PPTT_NODE_INDEXER),
+ IsGicCTokenEqual
+ );
+ // Duplicate GIC CPU Interface Token was found so two PPTT Processor Hierarchy
+ // Nodes map to the same MADT GICC structure
+ if (IsGicCTokenDuplicated) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ UniqueGicCRefCount = 0;
+
+ while (NodeCount-- != 0) {
+ ProcInfoNode = (CM_ARM_PROC_HIERARCHY_INFO*)ProcNodeIterator->Object;
+
+ // Check if the private resource count is within the size limit
+ // imposed on the Processor Hierarchy node by the specification.
+ // Note: The length field is 8 bit wide while the number of private
+ // resource field is 32 bit wide.
+ Length = GetProcHierarchyNodeSize (ProcInfoNode);
+ if (Length > MAX_UINT8) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Too many private resources. Count = %d. " \
+ "Maximum supported Processor Node size exceeded. " \
+ "Token = %p. Status = %r\n",
+ ProcInfoNode->NoOfPrivateResources,
+ ProcInfoNode->ParentToken,
+ Status
+ ));
+ return Status;
+ }
+
+ // Populate the node header
+ ProcStruct->Type = EFI_ACPI_6_3_PPTT_TYPE_PROCESSOR;
+ ProcStruct->Length = (UINT8)Length;
+ ProcStruct->Reserved[0] = EFI_ACPI_RESERVED_BYTE;
+ ProcStruct->Reserved[1] = EFI_ACPI_RESERVED_BYTE;
+
+ // Populate the flags
+ ProcStruct->Flags.PhysicalPackage = ProcInfoNode->Flags & BIT0;
+ ProcStruct->Flags.AcpiProcessorIdValid = (ProcInfoNode->Flags & BIT1) >> 1;
+ ProcStruct->Flags.ProcessorIsAThread = (ProcInfoNode->Flags & BIT2) >> 2;
+ ProcStruct->Flags.NodeIsALeaf = (ProcInfoNode->Flags & BIT3) >> 3;
+ ProcStruct->Flags.IdenticalImplementation =
+ (ProcInfoNode->Flags & BIT4) >> 4;
+ ProcStruct->Flags.Reserved = 0;
+
+ // Populate the parent reference
+ if (ProcInfoNode->ParentToken == CM_NULL_TOKEN) {
+ ProcStruct->Parent = 0;
+ } else {
+ Status = GetPpttNodeReferencedByToken (
+ Generator->ProcHierarchyNodeIndexedList,
+ Generator->ProcHierarchyNodeCount,
+ ProcInfoNode->ParentToken,
+ &PpttNodeFound
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get parent processor hierarchy node " \
+ "reference. Token = %p, Status = %r\n",
+ ProcInfoNode->ParentToken,
+ ProcInfoNode->Token,
+ Status
+ ));
+ return Status;
+ }
+
+ // Test if the reference is to a 'leaf' node
+ if (IS_PROC_NODE_LEAF (
+ ((CM_ARM_PROC_HIERARCHY_INFO*)PpttNodeFound->Object))) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Reference to a leaf Processor Hierarchy Node. " \
+ "ParentToken = %p. ChildToken = %p. Status = %r\n",
+ ProcInfoNode->ParentToken,
+ ProcInfoNode->Token,
+ Status
+ ));
+ return Status;
+ }
+
+ // Update Proc Structure with the offset of the parent node
+ ProcStruct->Parent = PpttNodeFound->Offset;
+
+ // Store the reference for the parent node in the Node Indexer
+ // so that this can be used later for cycle detection
+ ProcNodeIterator->TopologyParent = PpttNodeFound;
+ }
+
+ // Populate ACPI Processor ID
+ if (!IS_ACPI_PROC_ID_VALID (ProcInfoNode)) {
+ // Default invalid ACPI Processor ID to 0
+ ProcStruct->AcpiProcessorId = 0;
+ } else if (ProcInfoNode->GicCToken == CM_NULL_TOKEN) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The 'ACPI Processor ID valid' flag is set but no GICC " \
+ "structure token was provided. GicCToken = %p. RequestorToken = %p. " \
+ "Status = %r\n",
+ ProcInfoNode->GicCToken,
+ ProcInfoNode->Token,
+ Status
+ ));
+ return Status;
+ } else {
+ Status = GetEArmObjGicCInfo (
+ CfgMgrProtocol,
+ ProcInfoNode->GicCToken,
+ &GicCInfoList,
+ &GicCInfoCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get GICC structure. ACPI Processor ID " \
+ "can't be populated. GicCToken = %p. RequestorToken = %p. " \
+ "Status = %r\n",
+ ProcInfoNode->GicCToken,
+ ProcInfoNode->Token,
+ Status
+ ));
+ return Status;
+ }
+
+ if (GicCInfoCount != 1) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to find a unique GICC structure. " \
+ "ACPI Processor ID can't be populated. " \
+ "GICC Structure Count = %d. GicCToken = %p. RequestorToken = %p " \
+ "Status = %r\n",
+ GicCInfoCount,
+ ProcInfoNode->GicCToken,
+ ProcInfoNode->Token,
+ Status
+ ));
+ return Status;
+ }
+
+ // Update the ACPI Processor Id
+ ProcStruct->AcpiProcessorId = GicCInfoList->AcpiProcessorUid;
+
+ // Increment the reference count for the number of
+ // Unique GICC objects that were retrieved.
+ UniqueGicCRefCount++;
+ }
+
+ ProcStruct->NumberOfPrivateResources = ProcInfoNode->NoOfPrivateResources;
+ PrivateResources = (UINT32*)((UINT8*)ProcStruct +
+ sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR));
+
+ if (ProcStruct->NumberOfPrivateResources != 0) {
+ // Populate the private resources array
+ Status = AddPrivateResources (
+ Generator,
+ CfgMgrProtocol,
+ PrivateResources,
+ ProcStruct->NumberOfPrivateResources,
+ ProcInfoNode->PrivateResourcesArrayToken
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to populate the private resources array. " \
+ "Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+ }
+
+ // Next Processor Hierarchy Node
+ ProcStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR*)((UINT8*)ProcStruct +
+ ProcStruct->Length);
+ ProcNodeIterator++;
+ } // Processor Hierarchy Node
+
+ // Knowing the total number of GICC references made and that all GICC Token
+ // references are unique, we can test if no GICC instances have been left out.
+ Status = GetEArmObjGicCInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &GicCInfoList,
+ &GicCInfoCount
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get GICC Info. Status = %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ // MADT - PPTT cross validation
+ // This checks that one and only one GICC structure is referenced by a
+ // Processor Hierarchy Node in the PPTT.
+ // Since we have already checked that the GICC objects referenced by the
+ // Proc Nodes are unique, the UniqueGicCRefCount cannot be greater than
+ // the total number of GICC objects in the platform.
+ if (GicCInfoCount > UniqueGicCRefCount) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: %d GICC structure(s) exposed by MADT don't have " \
+ "a corresponding Processor Hierarchy Node. Status = %r\n",
+ GicCInfoCount - UniqueGicCRefCount,
+ Status
+ ));
+ }
+
+ return Status;
+}
+
+/**
+ Update the Cache Type Structure (Type 1) information.
+
+ This function populates the Cache Type Structures with information from
+ the Configuration Manager and adds this information to the PPTT table.
+
+ @param [in] Generator Pointer to the PPTT Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Pptt Pointer to PPTT table structure.
+ @param [in] NodesStartOffset Offset from the start of PPTT table to the
+ start of Cache Type Structures.
+
+ @retval EFI_SUCCESS Structures updated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND A required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddCacheTypeStructures (
+ IN CONST ACPI_PPTT_GENERATOR * CONST Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt,
+ IN CONST UINT32 NodesStartOffset
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE * CacheStruct;
+ PPTT_NODE_INDEXER * PpttNodeFound;
+ CM_ARM_CACHE_INFO * CacheInfoNode;
+ PPTT_NODE_INDEXER * CacheNodeIterator;
+ UINT32 NodeCount;
+
+ ASSERT (
+ (Generator != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (Pptt != NULL)
+ );
+
+ CacheStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE*)((UINT8*)Pptt +
+ NodesStartOffset);
+
+ CacheNodeIterator = Generator->CacheStructIndexedList;
+ NodeCount = Generator->CacheStructCount;
+
+ while (NodeCount-- != 0) {
+ CacheInfoNode = (CM_ARM_CACHE_INFO*)CacheNodeIterator->Object;
+
+ // Populate the node header
+ CacheStruct->Type = EFI_ACPI_6_3_PPTT_TYPE_CACHE;
+ CacheStruct->Length = sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE);
+ CacheStruct->Reserved[0] = EFI_ACPI_RESERVED_BYTE;
+ CacheStruct->Reserved[1] = EFI_ACPI_RESERVED_BYTE;
+
+ // "On Arm-based systems, all cache properties must be provided in the
+ // table." (ACPI 6.3, Section 5.2.29.2)
+ CacheStruct->Flags.SizePropertyValid = 1;
+ CacheStruct->Flags.NumberOfSetsValid = 1;
+ CacheStruct->Flags.AssociativityValid = 1;
+ CacheStruct->Flags.AllocationTypeValid = 1;
+ CacheStruct->Flags.CacheTypeValid = 1;
+ CacheStruct->Flags.WritePolicyValid = 1;
+ CacheStruct->Flags.LineSizeValid = 1;
+ CacheStruct->Flags.Reserved = 0;
+
+ // Populate the reference to the next level of cache
+ if (CacheInfoNode->NextLevelOfCacheToken == CM_NULL_TOKEN) {
+ CacheStruct->NextLevelOfCache = 0;
+ } else {
+ Status = GetPpttNodeReferencedByToken (
+ Generator->CacheStructIndexedList,
+ Generator->CacheStructCount,
+ CacheInfoNode->NextLevelOfCacheToken,
+ &PpttNodeFound
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get the reference to the Next Level of " \
+ "Cache. NextLevelOfCacheToken = %p. RequestorToken = %p. " \
+ "Status = %r\n",
+ CacheInfoNode->NextLevelOfCacheToken,
+ CacheInfoNode->Token,
+ Status
+ ));
+ return Status;
+ }
+
+ // Update Cache Structure with the offset for the next level of cache
+ CacheStruct->NextLevelOfCache = PpttNodeFound->Offset;
+
+ // Store the next level of cache information in the Node Indexer
+ // so that this can be used later for cycle detection
+ CacheNodeIterator->TopologyParent = PpttNodeFound;
+ }
+
+ CacheStruct->Size = CacheInfoNode->Size;
+
+ // Validate and populate the 'Number of sets' field
+ if (CacheInfoNode->NumberOfSets > PPTT_ARM_CCIDX_CACHE_NUMBER_OF_SETS_MAX) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: When ARMv8.3-CCIDX is implemented the maximum number " \
+ "of sets can be %d. NumberOfSets = %d. Status = %r\n",
+ PPTT_ARM_CCIDX_CACHE_NUMBER_OF_SETS_MAX,
+ CacheInfoNode->NumberOfSets,
+ Status
+ ));
+ return Status;
+ }
+
+ if (CacheInfoNode->NumberOfSets > PPTT_ARM_CACHE_NUMBER_OF_SETS_MAX) {
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: PPTT: When ARMv8.3-CCIDX is not implemented the maximum " \
+ "number of sets can be %d. NumberOfSets = %d\n",
+ PPTT_ARM_CACHE_NUMBER_OF_SETS_MAX,
+ CacheInfoNode->NumberOfSets
+ ));
+ }
+
+ CacheStruct->NumberOfSets = CacheInfoNode->NumberOfSets;
+
+ // Validate Associativity field based on maximum associativity
+ // supported by ACPI Cache type structure.
+ if (CacheInfoNode->Associativity > MAX_UINT8) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The maximum associativity supported by ACPI " \
+ "Cache type structure is %d. Associativity = %d, Status = %r\n",
+ MAX_UINT8,
+ CacheInfoNode->Associativity,
+ Status
+ ));
+ return Status;
+ }
+
+ // Validate the Associativity field based on the architecture specification
+ // The architecture supports much larger associativity values than the
+ // current ACPI specification.
+ // These checks will be needed in the future when the ACPI specification
+ // is extended. Disabling this code for now.
+#if 0
+ if (CacheInfoNode->Associativity > PPTT_ARM_CCIDX_CACHE_ASSOCIATIVITY_MAX) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: When ARMv8.3-CCIDX is implemented the maximum cache " \
+ "associativity can be %d. Associativity = %d. Status = %r\n",
+ PPTT_ARM_CCIDX_CACHE_ASSOCIATIVITY_MAX,
+ CacheInfoNode->Associativity,
+ Status
+ ));
+ return Status;
+ }
+
+ if (CacheInfoNode->Associativity > PPTT_ARM_CACHE_ASSOCIATIVITY_MAX) {
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: PPTT: When ARMv8.3-CCIDX is not implemented the maximum " \
+ "cache associativity can be %d. Associativity = %d\n",
+ PPTT_ARM_CACHE_ASSOCIATIVITY_MAX,
+ CacheInfoNode->Associativity
+ ));
+ }
+#endif
+
+ // Note a typecast is needed as the maximum associativity
+ // supported by ACPI Cache type structure is MAX_UINT8.
+ CacheStruct->Associativity = (UINT8)CacheInfoNode->Associativity;
+
+ // Populate cache attributes
+ CacheStruct->Attributes.AllocationType =
+ CacheInfoNode->Attributes & (BIT0 | BIT1);
+ CacheStruct->Attributes.CacheType =
+ (CacheInfoNode->Attributes & (BIT2 | BIT3)) >> 2;
+ CacheStruct->Attributes.WritePolicy =
+ (CacheInfoNode->Attributes & BIT4) >> 4;
+ CacheStruct->Attributes.Reserved = 0;
+
+ // Validate and populate cache line size
+ if ((CacheInfoNode->LineSize < PPTT_ARM_CACHE_LINE_SIZE_MIN) ||
+ (CacheInfoNode->LineSize > PPTT_ARM_CACHE_LINE_SIZE_MAX)) {
+
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The cache line size must be between %d and %d bytes " \
+ "on ARM Platforms. LineSize = %d. Status = %r\n" ,
+ PPTT_ARM_CACHE_LINE_SIZE_MIN,
+ PPTT_ARM_CACHE_LINE_SIZE_MAX,
+ CacheInfoNode->LineSize,
+ Status
+ ));
+ return Status;
+ }
+
+ if ((CacheInfoNode->LineSize & (CacheInfoNode->LineSize - 1)) != 0) {
+ Status = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: The cache line size is not a power of 2. " \
+ "LineSize = %d. Status = %r\n" ,
+ CacheInfoNode->LineSize,
+ Status
+ ));
+ return Status;
+ }
+
+ CacheStruct->LineSize = CacheInfoNode->LineSize;
+
+ // Next Cache Type Structure
+ CacheStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE*)((UINT8*)CacheStruct +
+ CacheStruct->Length);
+ CacheNodeIterator++;
+ } // Cache Type Structure
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Update the ID Type Structure (Type 2) information.
+
+ This function populates the ID Type Structures with information from
+ the Configuration Manager and and adds this information to the PPTT table.
+
+ @param [in] Generator Pointer to the PPTT Generator.
+ @param [in] CfgMgrProtocol Pointer to the Configuration Manager
+ Protocol Interface.
+ @param [in] Pptt Pointer to PPTT table structure.
+ @param [in] NodesStartOffset Offset from the start of PPTT table to the
+ start of ID Type Structures.
+
+ @retval EFI_SUCCESS Structures updated successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND A required object was not found.
+**/
+STATIC
+EFI_STATUS
+AddIdTypeStructures (
+ IN CONST ACPI_PPTT_GENERATOR * CONST Generator,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt,
+ IN CONST UINT32 NodesStartOffset
+ )
+{
+ EFI_ACPI_6_3_PPTT_STRUCTURE_ID * IdStruct;
+ CM_ARM_PROC_NODE_ID_INFO * ProcIdInfoNode;
+ PPTT_NODE_INDEXER * IdStructIterator;
+ UINT32 NodeCount;
+
+
+ ASSERT (
+ (Generator != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (Pptt != NULL)
+ );
+
+ IdStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_ID*)((UINT8*)Pptt + NodesStartOffset);
+
+ IdStructIterator = Generator->IdStructIndexedList;
+ NodeCount = Generator->IdStructCount;
+ while (NodeCount-- != 0) {
+ ProcIdInfoNode = (CM_ARM_PROC_NODE_ID_INFO*)IdStructIterator->Object;
+
+ // Populate the node
+ IdStruct->Type = EFI_ACPI_6_3_PPTT_TYPE_ID;
+ IdStruct->Length = sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_ID);
+ IdStruct->Reserved[0] = EFI_ACPI_RESERVED_BYTE;
+ IdStruct->Reserved[1] = EFI_ACPI_RESERVED_BYTE;
+ IdStruct->VendorId = ProcIdInfoNode->VendorId;
+ IdStruct->Level1Id = ProcIdInfoNode->Level1Id;
+ IdStruct->Level2Id = ProcIdInfoNode->Level2Id;
+ IdStruct->MajorRev = ProcIdInfoNode->MajorRev;
+ IdStruct->MinorRev = ProcIdInfoNode->MinorRev;
+ IdStruct->SpinRev = ProcIdInfoNode->SpinRev;
+
+ // Next ID Type Structure
+ IdStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_ID*)((UINT8*)IdStruct +
+ IdStruct->Length);
+ IdStructIterator++;
+ } // ID Type Structure
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Construct the PPTT 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 generator to be used.
+ @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
+BuildPpttTable (
+ 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;
+ UINT32 TableSize;
+ UINT32 ProcTopologyStructCount;
+ UINT32 ProcHierarchyNodeCount;
+ UINT32 CacheStructCount;
+ UINT32 IdStructCount;
+
+ UINT32 ProcHierarchyNodeOffset;
+ UINT32 CacheStructOffset;
+ UINT32 IdStructOffset;
+
+ CM_ARM_PROC_HIERARCHY_INFO * ProcHierarchyNodeList;
+ CM_ARM_CACHE_INFO * CacheStructList;
+ CM_ARM_PROC_NODE_ID_INFO * IdStructList;
+
+ ACPI_PPTT_GENERATOR * Generator;
+
+ // Pointer to the Node Indexer array
+ PPTT_NODE_INDEXER * NodeIndexer;
+
+ EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt;
+
+ ASSERT (
+ (This != NULL) &&
+ (AcpiTableInfo != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (Table != NULL) &&
+ (AcpiTableInfo->TableGeneratorId == This->GeneratorID) &&
+ (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature)
+ );
+
+ if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
+ (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Requested table revision = %d is not supported. "
+ "Supported table revisions: Minimum = %d. Maximum = %d\n",
+ AcpiTableInfo->AcpiTableRevision,
+ This->MinAcpiTableRevision,
+ This->AcpiTableRevision
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Generator = (ACPI_PPTT_GENERATOR*)This;
+ *Table = NULL;
+
+ // 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)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get processor hierarchy info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ ProcTopologyStructCount = ProcHierarchyNodeCount;
+ Generator->ProcHierarchyNodeCount = ProcHierarchyNodeCount;
+
+ // Get the cache info and update the processor topology structure count with
+ // Cache Type Structures (Type 1)
+ Status = GetEArmObjCacheInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &CacheStructList,
+ &CacheStructCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get cache info. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ ProcTopologyStructCount += CacheStructCount;
+ Generator->CacheStructCount = CacheStructCount;
+
+ // Get the processor hierarchy node ID info and update the processor topology
+ // structure count with ID Structures (Type 2)
+ Status = GetEArmObjProcNodeIdInfo (
+ CfgMgrProtocol,
+ CM_NULL_TOKEN,
+ &IdStructList,
+ &IdStructCount
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to get processor hierarchy node ID info. " \
+ "Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ ProcTopologyStructCount += IdStructCount;
+ Generator->IdStructCount = IdStructCount;
+
+ // Allocate Node Indexer array
+ NodeIndexer = (PPTT_NODE_INDEXER*)AllocateZeroPool (
+ sizeof (PPTT_NODE_INDEXER) *
+ ProcTopologyStructCount
+ );
+ if (NodeIndexer == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to allocate memory for Node Indexer. Status = %r\n ",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ DEBUG ((DEBUG_INFO, "INFO: NodeIndexer = %p\n", NodeIndexer));
+ Generator->ProcTopologyStructCount = ProcTopologyStructCount;
+ Generator->NodeIndexer = NodeIndexer;
+
+ // Calculate the size of the PPTT table
+ TableSize = sizeof (EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER);
+
+ // Include the size of Processor Hierarchy Nodes and index them
+ if (Generator->ProcHierarchyNodeCount != 0) {
+ ProcHierarchyNodeOffset = TableSize;
+ Generator->ProcHierarchyNodeIndexedList = NodeIndexer;
+ TableSize += GetSizeofProcHierarchyNodes (
+ ProcHierarchyNodeOffset,
+ ProcHierarchyNodeList,
+ Generator->ProcHierarchyNodeCount,
+ &NodeIndexer
+ );
+
+ DEBUG ((
+ DEBUG_INFO,
+ " ProcHierarchyNodeCount = %d\n" \
+ " ProcHierarchyNodeOffset = 0x%x\n" \
+ " ProcHierarchyNodeIndexedList = 0x%p\n",
+ Generator->ProcHierarchyNodeCount,
+ ProcHierarchyNodeOffset,
+ Generator->ProcHierarchyNodeIndexedList
+ ));
+
+ }
+
+ // Include the size of Cache Type Structures and index them
+ if (Generator->CacheStructCount != 0) {
+ CacheStructOffset = TableSize;
+ Generator->CacheStructIndexedList = NodeIndexer;
+ TableSize += GetSizeofCacheTypeStructs (
+ CacheStructOffset,
+ CacheStructList,
+ Generator->CacheStructCount,
+ &NodeIndexer
+ );
+ DEBUG ((
+ DEBUG_INFO,
+ " CacheStructCount = %d\n" \
+ " CacheStructOffset = 0x%x\n" \
+ " CacheStructIndexedList = 0x%p\n",
+ Generator->CacheStructCount,
+ CacheStructOffset,
+ Generator->CacheStructIndexedList
+ ));
+ }
+
+ // Include the size of ID Type Structures and index them
+ if (Generator->IdStructCount != 0) {
+ IdStructOffset = TableSize;
+ Generator->IdStructIndexedList = NodeIndexer;
+ TableSize += GetSizeofIdStructs (
+ IdStructOffset,
+ IdStructList,
+ Generator->IdStructCount,
+ &NodeIndexer
+ );
+ DEBUG ((
+ DEBUG_INFO,
+ " IdStructCount = %d\n" \
+ " IdStructOffset = 0x%x\n" \
+ " IdStructIndexedList = 0x%p\n",
+ Generator->IdStructCount,
+ IdStructOffset,
+ Generator->IdStructIndexedList
+ ));
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "INFO: PPTT:\n" \
+ " ProcTopologyStructCount = %d\n" \
+ " TableSize = %d\n",
+ ProcTopologyStructCount,
+ TableSize
+ ));
+
+ // Allocate the Buffer for the PPTT table
+ *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize);
+ if (*Table == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to allocate memory for PPTT Table. " \
+ "Size = %d. Status = %r\n",
+ TableSize,
+ Status
+ ));
+ goto error_handler;
+ }
+
+ Pptt = (EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER*)*Table;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "PPTT: Pptt = 0x%p. TableSize = 0x%x\n",
+ Pptt,
+ TableSize
+ ));
+
+ // Add ACPI header
+ Status = AddAcpiHeader (
+ CfgMgrProtocol,
+ This,
+ &Pptt->Header,
+ AcpiTableInfo,
+ TableSize
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to add ACPI header. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ // Add Processor Hierarchy Nodes (Type 0) to the generated table
+ if (Generator->ProcHierarchyNodeCount != 0) {
+ Status = AddProcHierarchyNodes (
+ Generator,
+ CfgMgrProtocol,
+ Pptt,
+ ProcHierarchyNodeOffset
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to add Processor Hierarchy Nodes. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ // Add Cache Type Structures (Type 1) to the generated table
+ if (Generator->CacheStructCount != 0) {
+ Status = AddCacheTypeStructures (
+ Generator,
+ CfgMgrProtocol,
+ Pptt,
+ CacheStructOffset
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to add Cache Type Structures. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ // Add ID Type Structures (Type 2) to the generated table
+ if (Generator->IdStructCount != 0) {
+ Status = AddIdTypeStructures (
+ Generator,
+ CfgMgrProtocol,
+ Pptt,
+ IdStructOffset
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Failed to add ID Type Structures. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+ }
+
+ // Validate CM object cross-references in PPTT
+ Status = DetectCyclesInTopology (Generator);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: PPTT: Invalid processor and cache topology. Status = %r\n",
+ Status
+ ));
+ goto error_handler;
+ }
+
+ return Status;
+
+error_handler:
+ if (Generator->NodeIndexer != NULL) {
+ FreePool (Generator->NodeIndexer);
+ Generator->NodeIndexer = NULL;
+ }
+
+ if (*Table != NULL) {
+ FreePool (*Table);
+ *Table = NULL;
+ }
+
+ return Status;
+}
+
+/**
+ Free any resources allocated for constructing the PPTT
+
+ @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
+FreePpttTableResources (
+ IN CONST ACPI_TABLE_GENERATOR * CONST This,
+ IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo,
+ IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
+ IN OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table
+ )
+{
+ ACPI_PPTT_GENERATOR * Generator;
+
+ ASSERT (
+ (This != NULL) &&
+ (AcpiTableInfo != NULL) &&
+ (CfgMgrProtocol != NULL) &&
+ (AcpiTableInfo->TableGeneratorId == This->GeneratorID) &&
+ (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature)
+ );
+
+ Generator = (ACPI_PPTT_GENERATOR*)This;
+
+ // Free any memory allocated by the generator
+ if (Generator->NodeIndexer != NULL) {
+ FreePool (Generator->NodeIndexer);
+ Generator->NodeIndexer = NULL;
+ }
+
+ if ((Table == NULL) || (*Table == NULL)) {
+ DEBUG ((DEBUG_ERROR, "ERROR: PPTT: Invalid Table Pointer\n"));
+ ASSERT (
+ (Table != NULL) &&
+ (*Table != NULL)
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (*Table);
+ *Table = NULL;
+ return EFI_SUCCESS;
+}
+
+/** The PPTT Table Generator revision.
+*/
+#define PPTT_GENERATOR_REVISION CREATE_REVISION (1, 0)
+
+/** The interface for the PPTT Table Generator.
+*/
+STATIC
+ACPI_PPTT_GENERATOR PpttGenerator = {
+ // ACPI table generator header
+ {
+ // Generator ID
+ CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdPptt),
+ // Generator Description
+ L"ACPI.STD.PPTT.GENERATOR",
+ // ACPI Table Signature
+ EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_STRUCTURE_SIGNATURE,
+ // ACPI Table Revision supported by this Generator
+ EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_REVISION,
+ // Minimum supported ACPI Table Revision
+ EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_REVISION,
+ // Creator ID
+ TABLE_GENERATOR_CREATOR_ID_ARM,
+ // Creator Revision
+ PPTT_GENERATOR_REVISION,
+ // Build Table function
+ BuildPpttTable,
+ // Free Resource function
+ FreePpttTableResources,
+ // Extended build function not needed
+ NULL,
+ // Extended build function not implemented by the generator.
+ // Hence extended free resource function is not required.
+ NULL
+ },
+
+ // PPTT Generator private data
+
+ // Processor topology node count
+ 0,
+ // Count of Processor Hierarchy Nodes
+ 0,
+ // Count of Cache Structures
+ 0,
+ // Count of Id Structures
+ 0,
+ // Pointer to PPTT Node Indexer
+ NULL
+};
+
+/**
+ Register the Generator with the ACPI Table Factory.
+
+ @param [in] ImageHandle The handle to the image.
+ @param [in] SystemTable Pointer to the System Table.
+
+ @retval EFI_SUCCESS The Generator is registered.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_ALREADY_STARTED The Generator for the Table ID
+ is already registered.
+**/
+EFI_STATUS
+EFIAPI
+AcpiPpttLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = RegisterAcpiTableGenerator (&PpttGenerator.Header);
+ DEBUG ((DEBUG_INFO, "PPTT: 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
+AcpiPpttLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE * SystemTable
+ )
+{
+ EFI_STATUS Status;
+ Status = DeregisterAcpiTableGenerator (&PpttGenerator.Header);
+ DEBUG ((DEBUG_INFO, "PPTT: Deregister Generator. Status = %r\n", Status));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}