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