diff --git a/DynamicTablesPkg/DynamicTables.dsc.inc b/DynamicTablesPkg/DynamicTables.dsc.inc index 8ea5b0a7d9..142832b9fa 100644 --- a/DynamicTablesPkg/DynamicTables.dsc.inc +++ b/DynamicTablesPkg/DynamicTables.dsc.inc @@ -1,7 +1,7 @@ ## @file # Dsc include file for Dynamic Tables Framework. # -# Copyright (c) 2017 - 2018, ARM Limited. All rights reserved.
+# Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
# # SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -27,6 +27,7 @@ NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/AcpiIortLibArm.inf NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiMadtLibArm/AcpiMadtLibArm.inf NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiMcfgLibArm/AcpiMcfgLibArm.inf + NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/AcpiPpttLibArm.inf NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiRawLibArm/AcpiRawLibArm.inf NULL|DynamicTablesPkg/Library/Acpi/Arm/AcpiSpcrLibArm/AcpiSpcrLibArm.inf } diff --git a/DynamicTablesPkg/Include/AcpiTableGenerator.h b/DynamicTablesPkg/Include/AcpiTableGenerator.h index c4ef05965f..7d6d344227 100644 --- a/DynamicTablesPkg/Include/AcpiTableGenerator.h +++ b/DynamicTablesPkg/Include/AcpiTableGenerator.h @@ -51,6 +51,8 @@ The Dynamic Tables Framework implements the following ACPI table generators: from the Configuration Manager and builds the MCFG table. - IORT : The IORT generator collates the IO Topology information from the Configuration Manager and builds the IORT table. + - PPTT : The PPTT generator collates the processor topology information from + the Configuration Manager and builds the PPTT table. */ /** The ACPI_TABLE_GENERATOR_ID type describes ACPI table generator ID. @@ -72,6 +74,7 @@ typedef enum StdAcpiTableId { EStdAcpiTableIdSpcr, ///< SPCR Generator EStdAcpiTableIdMcfg, ///< MCFG Generator EStdAcpiTableIdIort, ///< IORT Generator + EStdAcpiTableIdPptt, ///< PPTT Generator EStdAcpiTableIdMax } ESTD_ACPI_TABLE_ID; diff --git a/DynamicTablesPkg/Include/ArmNameSpaceObjects.h b/DynamicTablesPkg/Include/ArmNameSpaceObjects.h index d9dcca12db..19c47ef655 100644 --- a/DynamicTablesPkg/Include/ArmNameSpaceObjects.h +++ b/DynamicTablesPkg/Include/ArmNameSpaceObjects.h @@ -48,6 +48,10 @@ typedef enum ArmObjectID { EArmObjGicItsIdentifierArray, ///< 24 - GIC ITS Identifier Array EArmObjIdMappingArray, ///< 25 - ID Mapping Array EArmObjSmmuInterruptArray, ///< 26 - SMMU Interrupt Array + EArmObjProcHierarchyInfo, ///< 27 - Processor Hierarchy Info + EArmObjCacheInfo, ///< 28 - Cache Info + EArmObjProcNodeIdInfo, ///< 29 - Processor Hierarchy Node ID Info + EArmObjCmRef, ///< 30 - CM Object Reference EArmObjMax } EARM_OBJECT_ID; @@ -628,6 +632,97 @@ typedef struct CmArmSmmuInterrupt { UINT32 Flags; } CM_ARM_SMMU_INTERRUPT; +/** A structure that describes the Processor Hierarchy Node (Type 0) in PPTT + + ID: EArmObjProcHierarchyInfo +*/ +typedef struct CmArmProcHierarchyInfo { + /// A unique token used to identify this object + CM_OBJECT_TOKEN Token; + /// Processor structure flags (ACPI 6.3 - January 2019, PPTT, Table 5-155) + UINT32 Flags; + /// Token for the parent CM_ARM_PROC_HIERARCHY_INFO object in the processor + /// topology. A value of CM_NULL_TOKEN means this node has no parent. + CM_OBJECT_TOKEN ParentToken; + /// Token of the associated CM_ARM_GICC_INFO object which has the + /// corresponding ACPI Processor ID. A value of CM_NULL_TOKEN means this + /// node represents a group of associated processors and it does not have an + /// associated GIC CPU interface. + CM_OBJECT_TOKEN GicCToken; + /// Number of resources private to this Node + UINT32 NoOfPrivateResources; + /// Token of the array which contains references to the resources private to + /// this CM_ARM_PROC_HIERARCHY_INFO instance. This field is ignored if + /// the NoOfPrivateResources is 0, in which case it is recomended to set + /// this field to CM_NULL_TOKEN. + CM_OBJECT_TOKEN PrivateResourcesArrayToken; +} CM_ARM_PROC_HIERARCHY_INFO; + +/** A structure that describes the Cache Type Structure (Type 1) in PPTT + + ID: EArmObjCacheInfo +*/ +typedef struct CmArmCacheInfo { + /// A unique token used to identify this object + CM_OBJECT_TOKEN Token; + /// Reference token for the next level of cache that is private to the same + /// CM_ARM_PROC_HIERARCHY_INFO instance. A value of CM_NULL_TOKEN means this + /// entry represents the last cache level appropriate to the processor + /// hierarchy node structures using this entry. + CM_OBJECT_TOKEN NextLevelOfCacheToken; + /// Size of the cache in bytes + UINT32 Size; + /// Number of sets in the cache + UINT32 NumberOfSets; + /// Integer number of ways. The maximum associativity supported by + /// ACPI Cache type structure is limited to MAX_UINT8. However, + /// the maximum number of ways supported by the architecture is + /// PPTT_ARM_CCIDX_CACHE_ASSOCIATIVITY_MAX. Therfore this field + /// is 32-bit wide. + UINT32 Associativity; + /// Cache attributes (ACPI 6.3 - January 2019, PPTT, Table 5-156) + UINT8 Attributes; + /// Line size in bytes + UINT16 LineSize; +} CM_ARM_CACHE_INFO; + +/** A structure that describes the ID Structure (Type 2) in PPTT + + ID: EArmObjProcNodeIdInfo +*/ +typedef struct CmArmProcNodeIdInfo { + /// A unique token used to identify this object + CM_OBJECT_TOKEN Token; + // Vendor ID (as described in ACPI ID registry) + UINT32 VendorId; + /// First level unique node ID + UINT64 Level1Id; + /// Second level unique node ID + UINT64 Level2Id; + /// Major revision of the node + UINT16 MajorRev; + /// Minor revision of the node + UINT16 MinorRev; + /// Spin revision of the node + UINT16 SpinRev; +} CM_ARM_PROC_NODE_ID_INFO; + +/** A structure that describes a reference to another Configuration Manager + object. + + This is useful for creating an array of reference tokens. The framework + can then query the configuration manager for these arrays using the + object ID EArmObjCmRef. + + This can be used is to represent one-to-many relationships between objects. + + ID: EArmObjCmRef +*/ +typedef struct CmArmObjRef { + /// Token of the CM object being referenced + CM_OBJECT_TOKEN ReferenceToken; +} CM_ARM_OBJ_REF; + #pragma pack() #endif // ARM_NAMESPACE_OBJECTS_H_ diff --git a/DynamicTablesPkg/Include/ConfigurationManagerObject.h b/DynamicTablesPkg/Include/ConfigurationManagerObject.h index 3bd4273d25..b0d3e709ec 100644 --- a/DynamicTablesPkg/Include/ConfigurationManagerObject.h +++ b/DynamicTablesPkg/Include/ConfigurationManagerObject.h @@ -63,10 +63,24 @@ Object ID's in the ARM Namespace: 10 - Serial Debug Port Info 11 - Generic Timer Info 12 - Platform GT Block Info - 13 - Platform Generic Watchdog - 14 - PCI Configuration Space Info - 15 - Hypervisor Vendor Id - 16 - Fixed feature flags for FADT + 13 - Generic Timer Block Frame Info + 14 - Platform Generic Watchdog + 15 - PCI Configuration Space Info + 16 - Hypervisor Vendor Id + 17 - Fixed feature flags for FADT + 18 - ITS Group + 19 - Named Component + 20 - Root Complex + 21 - SMMUv1 or SMMUv2 + 22 - SMMUv3 + 23 - PMCG + 24 - GIC ITS Identifier Array + 25 - ID Mapping Array + 26 - SMMU Interrupt Array + 27 - Processor Hierarchy Info + 28 - Cache Info + 29 - Processor Hierarchy Node ID Info + 30 - CM Object Reference */ typedef UINT32 CM_OBJECT_ID; diff --git a/DynamicTablesPkg/Include/TableGenerator.h b/DynamicTablesPkg/Include/TableGenerator.h index d39868a41c..da6434a48b 100644 --- a/DynamicTablesPkg/Include/TableGenerator.h +++ b/DynamicTablesPkg/Include/TableGenerator.h @@ -1,6 +1,6 @@ /** @file - Copyright (c) 2017, ARM Limited. All rights reserved. + Copyright (c) 2017 - 2019, ARM Limited. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent @@ -55,6 +55,7 @@ _______________________________________________________________________________ 7 - DBG2 8 - SPCR 9 - MCFG + 10 - PPTT Standard SMBIOS Table IDs: 0 - Reserved diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/AcpiPpttLibArm.inf b/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/AcpiPpttLibArm.inf new file mode 100644 index 0000000000..3cb13d7d8f --- /dev/null +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/AcpiPpttLibArm.inf @@ -0,0 +1,29 @@ +## @file +# PPTT Table Generator +# +# Copyright (c) 2019, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = AcpiPpttLibArm + FILE_GUID = FA102D52-5A92-4F95-A097-1D53F9CF5959 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_DRIVER + LIBRARY_CLASS = NULL|DXE_DRIVER + CONSTRUCTOR = AcpiPpttLibConstructor + DESTRUCTOR = AcpiPpttLibDestructor + +[Sources] + PpttGenerator.c + +[Packages] + EmbeddedPkg/EmbeddedPkg.dec + DynamicTablesPkg/DynamicTablesPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c b/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c new file mode 100644 index 0000000000..c8713dec62 --- /dev/null +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.c @@ -0,0 +1,1528 @@ +/** @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 +#include +#include +#include +#include + +// Module specific include files. +#include +#include +#include +#include +#include + +#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); + + // + + 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; + + 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. + if ((sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR) + + (ProcInfoNode->NoOfPrivateResources * sizeof (UINT32))) > 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 = GetProcHierarchyNodeSize (ProcInfoNode); + 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 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, + &Generator->ProcHierarchyNodeCount + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ERROR: PPTT: Failed to get processor hierarchy info. Status = %r\n", + Status + )); + goto error_handler; + } + + ProcTopologyStructCount = Generator->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, + &Generator->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 += Generator->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, + &Generator->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 += Generator->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 + ); + } + + // 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 + ); + } + + // 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, + "INFO: PPTT:\n" \ + " ProcTopologyStructCount = %d\n" \ + " TableSize = %d\n", + ProcTopologyStructCount, + TableSize + )); + + DEBUG (( + DEBUG_INFO, + " ProcHierarchyNodeCount = %d\n" \ + " ProcHierarchyNodeOffset = 0x%x\n" \ + " ProcHierarchyNodeIndexedList = 0x%p\n", + Generator->ProcHierarchyNodeCount, + ProcHierarchyNodeOffset, + Generator->ProcHierarchyNodeIndexedList + )); + + DEBUG (( + DEBUG_INFO, + " CacheStructCount = %d\n" \ + " CacheStructOffset = 0x%x\n" \ + " CacheStructIndexedList = 0x%p\n", + Generator->CacheStructCount, + CacheStructOffset, + Generator->CacheStructIndexedList + )); + + DEBUG (( + DEBUG_INFO, + " IdStructCount = %d\n" \ + " IdStructOffset = 0x%x\n" \ + " IdStructIndexedList = 0x%p\n", + Generator->IdStructCount, + IdStructOffset, + Generator->IdStructIndexedList + )); + + // 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, + // 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 CONST EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * CONST 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 CONST EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * CONST SystemTable + ) +{ + EFI_STATUS Status; + Status = DeregisterAcpiTableGenerator (&PpttGenerator.Header); + DEBUG ((DEBUG_INFO, "PPTT: Deregister Generator. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.h b/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.h new file mode 100644 index 0000000000..6a0fdd08e1 --- /dev/null +++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiPpttLibArm/PpttGenerator.h @@ -0,0 +1,190 @@ +/** @file + Header file for the dynamic PPTT 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 + - ARM Architecture Reference Manual ARMv8 (D.a) + + @par Glossary: + - Cm or CM - Configuration Manager + - Obj or OBJ - Object +**/ + +#ifndef PPTT_GENERATOR_H_ +#define PPTT_GENERATOR_H_ + +#pragma pack(1) + +/// Cache parameters allowed by the architecture with +/// ARMv8.3-CCIDX (Cache extended number of sets) +/// Derived from CCSIDR_EL1 when ID_AA64MMFR2_EL1.CCIDX==0001 +#define PPTT_ARM_CCIDX_CACHE_NUMBER_OF_SETS_MAX (1 << 24) +#define PPTT_ARM_CCIDX_CACHE_ASSOCIATIVITY_MAX (1 << 21) + +/// Cache parameters allowed by the architecture without +/// ARMv8.3-CCIDX (Cache extended number of sets) +/// Derived from CCSIDR_EL1 when ID_AA64MMFR2_EL1.CCIDX==0000 +#define PPTT_ARM_CACHE_NUMBER_OF_SETS_MAX (1 << 15) +#define PPTT_ARM_CACHE_ASSOCIATIVITY_MAX (1 << 10) + +/// Common cache parameters +/// Derived from CCSIDR_EL1 +/// The LineSize is represented by bits 2:0 +/// (Log2(Number of bytes in cache line)) - 4 is used to represent +/// the LineSize bits. +#define PPTT_ARM_CACHE_LINE_SIZE_MAX (1 << 11) +#define PPTT_ARM_CACHE_LINE_SIZE_MIN (1 << 4) + +/// Test if the given Processor Hierarchy Info object has the 'Node is a Leaf' +/// flag set +#define IS_PROC_NODE_LEAF(Node) ((Node->Flags & BIT3) != 0) + +/// Test if the given Processor Hierarchy Info object has the 'ACPI Processor +/// ID valid' flag set +#define IS_ACPI_PROC_ID_VALID(Node) ((Node->Flags & BIT1) != 0) + +/** + The GET_SIZE_OF_PPTT_STRUCTS macro expands to a function that is used to + calculate the total memory requirement for the PPTT structures represented + by the given list of Configuration Manager Objects of the same type. This + function also indexes the input CM objects so that various other CM objects + (possibly of different type) can reference them. + + The size of memory needed for the specified type of PPTT structures is based + on the number and type of CM objects provided. The macro assumes that the + ACPI object PpttObjName has fixed size. + + The macro expands to a function which has the following prototype: + + STATIC + UINT32 + EFIAPI + GetSizeof ( + IN CONST UINT32 StartOffset, + IN CONST CmObjectType * Nodes, + IN UINT32 NodeCount, + IN OUT PPTT_NODE_INDEXER ** CONST NodeIndexer + ) + + Generated function parameters: + @param [in] StartOffset Offset from the start of PPTT to where + the PPTT structures will be placed. + @param [in] NodesToIndex Pointer to the list of CM objects to be + indexed and size-estimated. + @param [out] NodeCount Number of CM objects in NodesToIndex. + @param [in, out] NodeIndexer Pointer to the list of Node Indexer + elements to populate. + @retval Size Total memory requirement for the PPTT + structures described in NodesToIndex. + + Macro Parameters: + @param [in] PpttObjName Name for the type of PPTT structures which + size is estimated. + @param [in] PpttObjSize Expression to use to calculate the size of + of a single instance of the PPTT structure + which corresponds to the CM object being + indexed. + @param [in] CmObjectType Data type of the CM nodes in NodesToIndex. +**/ +#define GET_SIZE_OF_PPTT_STRUCTS( \ + PpttObjName, \ + PpttObjSize, \ + CmObjectType \ +) \ +STATIC \ +UINT32 \ +GetSizeof##PpttObjName ( \ + IN CONST UINT32 StartOffset, \ + IN CONST CmObjectType * NodesToIndex, \ + IN UINT32 NodeCount, \ + IN OUT PPTT_NODE_INDEXER ** CONST NodeIndexer \ + ) \ +{ \ + UINT32 Size; \ + \ + ASSERT ( \ + (NodesToIndex != NULL) && \ + (NodeIndexer != NULL) \ + ); \ + \ + Size = 0; \ + while (NodeCount-- != 0) { \ + (*NodeIndexer)->Token = NodesToIndex->Token; \ + (*NodeIndexer)->Object = (VOID*)NodesToIndex; \ + (*NodeIndexer)->Offset = Size + StartOffset; \ + (*NodeIndexer)->CycleDetectionStamp = 0; \ + (*NodeIndexer)->TopologyParent = NULL; \ + DEBUG (( \ + DEBUG_INFO, \ + "PPTT: Node Indexer = %p, Token = %p, Object = %p, Offset = 0x%x\n", \ + *NodeIndexer, \ + (*NodeIndexer)->Token, \ + (*NodeIndexer)->Object, \ + (*NodeIndexer)->Offset \ + )); \ + \ + Size += PpttObjSize; \ + (*NodeIndexer)++; \ + NodesToIndex++; \ + } \ + return Size; \ +} + +/** + A structure for indexing CM objects (nodes) used in PPTT generation. + + PPTT_NODE_INDEXER is a wrapper around CM objects which augments these objects + with additional information that enables generating PPTT structures with + correct cross-references. + + PPTT_NODE_INDEXER keeps track of each structure's offset from the base + address of the generated table. It also caches certain information and makes + PPTT cyclic reference detection possible. +*/ +typedef struct PpttNodeIndexer { + /// Unique identifier for the node + CM_OBJECT_TOKEN Token; + /// Pointer to the CM object being indexed + VOID * Object; + /// Offset from the start of the PPTT table to the PPTT structure which is + /// represented by Object + UINT32 Offset; + /// Field used to mark nodes as 'visited' when detecting cycles in processor + /// and cache topology + UINT32 CycleDetectionStamp; + /// Reference to a Node Indexer element which is the parent of this Node + /// Indexer element in the processor and cache topology + /// e.g For a hardware thread the TopologyParent would point to a CPU node + /// For a L1 cache the TopologyParent would point to a L2 cache + struct PpttNodeIndexer * TopologyParent; +} PPTT_NODE_INDEXER; + +typedef struct AcpiPpttGenerator { + /// ACPI Table generator header + ACPI_TABLE_GENERATOR Header; + /// PPTT structure count + UINT32 ProcTopologyStructCount; + /// List of indexed CM objects for PPTT generation + PPTT_NODE_INDEXER * NodeIndexer; + /// Pointer to the start of Processor Hierarchy nodes in + /// the Node Indexer array + PPTT_NODE_INDEXER * ProcHierarchyNodeIndexedList; + /// Pointer to the start of Cache Structures in the Node Indexer array + PPTT_NODE_INDEXER * CacheStructIndexedList; + /// Pointer to the start of Id Structures in the Node Indexer array + PPTT_NODE_INDEXER * IdStructIndexedList; + /// Count of Processor Hierarchy Nodes + UINT32 ProcHierarchyNodeCount; + /// Count of Cache Structures + UINT32 CacheStructCount; + /// Count of Id Structures + UINT32 IdStructCount; + +} ACPI_PPTT_GENERATOR; + +#pragma pack() + +#endif // PPTT_GENERATOR_H_