| /** @file | |
| PPTT Table Generator | |
| Copyright (c) 2021, ARM Limited. All rights reserved. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| @par Reference(s): | |
| - ACPI 6.4 Specification, January 2021 | |
| @par Glossary: | |
| - Cm or CM - Configuration Manager | |
| - Obj or OBJ - Object | |
| **/ | |
| #include <Library/AcpiLib.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Protocol/AcpiTable.h> | |
| // Module specific include files. | |
| #include <AcpiTableGenerator.h> | |
| #include <ConfigurationManagerObject.h> | |
| #include <ConfigurationManagerHelper.h> | |
| #include <MetadataHelpers.h> | |
| #include <Library/CmObjHelperLib.h> | |
| #include <Library/TableHelperLib.h> | |
| #include <Library/MetadataHandlerLib.h> | |
| #include <Protocol/ConfigurationManagerProtocol.h> | |
| #include "PpttGenerator.h" | |
| /** | |
| ARM standard PPTT Generator | |
| Requirements: | |
| The following Configuration Manager Object(s) are used by this Generator: | |
| - EArchCommonObjProcHierarchyInfo (REQUIRED) | |
| - EArchCommonObjCacheInfo | |
| - EArchCommonObjCmRef | |
| - EArmObjGicCInfo (REQUIRED) | |
| */ | |
| /** | |
| This macro expands to a function that retrieves the Processor Hierarchy | |
| information from the Configuration Manager. | |
| */ | |
| GET_OBJECT_LIST ( | |
| EObjNameSpaceArchCommon, | |
| EArchCommonObjProcHierarchyInfo, | |
| CM_ARCH_COMMON_PROC_HIERARCHY_INFO | |
| ); | |
| /** | |
| This macro expands to a function that retrieves the cache information | |
| from the Configuration Manager. | |
| */ | |
| GET_OBJECT_LIST ( | |
| EObjNameSpaceArchCommon, | |
| EArchCommonObjCacheInfo, | |
| CM_ARCH_COMMON_CACHE_INFO | |
| ); | |
| /** | |
| This macro expands to a function that retrieves the cross-CM-object- | |
| reference information from the Configuration Manager. | |
| */ | |
| GET_OBJECT_LIST ( | |
| EObjNameSpaceArchCommon, | |
| EArchCommonObjCmRef, | |
| CM_ARCH_COMMON_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_ARCH_COMMON_PROC_HIERARCHY_INFO *Node | |
| ) | |
| { | |
| ASSERT (Node != NULL); | |
| // <size of Processor Hierarchy Node> + <size of Private Resources array> | |
| return sizeof (EFI_ACPI_6_4_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_ARCH_COMMON_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_4_PPTT_STRUCTURE_CACHE), | |
| CM_ARCH_COMMON_CACHE_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_ARCH_COMMON_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_ARCH_COMMON_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 = GetEArchCommonObjCmRef ( | |
| 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. 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 | |
| AcpiIdObjectToken. | |
| @retval FALSE Object1 and Object2 have different | |
| AcpiIdObjectTokens. | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| IsAcpiIdObjectTokenEqual ( | |
| IN CONST VOID *Object1, | |
| IN CONST VOID *Object2, | |
| IN UINTN Index1, | |
| IN UINTN Index2 | |
| ) | |
| { | |
| PPTT_NODE_INDEXER *IndexedObject1; | |
| PPTT_NODE_INDEXER *IndexedObject2; | |
| CM_ARCH_COMMON_PROC_HIERARCHY_INFO *ProcNode1; | |
| CM_ARCH_COMMON_PROC_HIERARCHY_INFO *ProcNode2; | |
| ASSERT ( | |
| (Object1 != NULL) && | |
| (Object2 != NULL) | |
| ); | |
| IndexedObject1 = (PPTT_NODE_INDEXER *)Object1; | |
| IndexedObject2 = (PPTT_NODE_INDEXER *)Object2; | |
| ProcNode1 = (CM_ARCH_COMMON_PROC_HIERARCHY_INFO *)IndexedObject1->Object; | |
| ProcNode2 = (CM_ARCH_COMMON_PROC_HIERARCHY_INFO *)IndexedObject2->Object; | |
| if (IS_ACPI_PROC_ID_VALID (ProcNode1) && | |
| IS_ACPI_PROC_ID_VALID (ProcNode2) && | |
| (ProcNode1->AcpiIdObjectToken != CM_NULL_TOKEN) && | |
| (ProcNode2->AcpiIdObjectToken != CM_NULL_TOKEN) && | |
| (ProcNode1->AcpiIdObjectToken == ProcNode2->AcpiIdObjectToken)) | |
| { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: PPTT: Two Processor Hierarchy Info objects (%d and %d) map to " \ | |
| "the same ACPI ID reference object. ACPI Processor IDs are not unique. " \ | |
| "AcpiIdObjectToken = %p.\n", | |
| Index1, | |
| Index2, | |
| ProcNode1->AcpiIdObjectToken | |
| )); | |
| 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_4_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER *Pptt, | |
| IN CONST UINT32 NodesStartOffset | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_ACPI_6_4_PPTT_STRUCTURE_PROCESSOR *ProcStruct; | |
| UINT32 *PrivateResources; | |
| BOOLEAN IsAcpiIdObjectTokenDuplicated; | |
| CM_ARM_GICC_INFO *GicCInfoList; | |
| UINT32 GicCInfoCount; | |
| UINT32 UniqueGicCRefCount; | |
| PPTT_NODE_INDEXER *PpttNodeFound; | |
| CM_ARCH_COMMON_PROC_HIERARCHY_INFO *ProcInfoNode; | |
| PPTT_NODE_INDEXER *ProcNodeIterator; | |
| UINT32 NodeCount; | |
| UINT32 Length; | |
| METADATA_OBJ_UID MetadataUid; | |
| BOOLEAN SsdtCpuTopoPresent; | |
| ASSERT ( | |
| (Generator != NULL) && | |
| (CfgMgrProtocol != NULL) && | |
| (Pptt != NULL) | |
| ); | |
| ProcStruct = (EFI_ACPI_6_4_PPTT_STRUCTURE_PROCESSOR *)((UINT8 *)Pptt + | |
| NodesStartOffset); | |
| ProcNodeIterator = Generator->ProcHierarchyNodeIndexedList; | |
| NodeCount = Generator->ProcHierarchyNodeCount; | |
| SsdtCpuTopoPresent = CheckAcpiTablePresent (CfgMgrProtocol, EStdAcpiTableIdSsdtCpuTopology); | |
| // Check if every GICC Object is referenced by onlu one Proc Node | |
| IsAcpiIdObjectTokenDuplicated = FindDuplicateValue ( | |
| ProcNodeIterator, | |
| NodeCount, | |
| sizeof (PPTT_NODE_INDEXER), | |
| IsAcpiIdObjectTokenEqual | |
| ); | |
| // Duplicate GIC CPU Interface Token was found so two PPTT Processor Hierarchy | |
| // Nodes map to the same MADT GICC structure | |
| if (IsAcpiIdObjectTokenDuplicated) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| UniqueGicCRefCount = 0; | |
| while (NodeCount-- != 0) { | |
| ProcInfoNode = (CM_ARCH_COMMON_PROC_HIERARCHY_INFO *)ProcNodeIterator->Object; | |
| // Check if the private resource count is within the size limit | |
| // imposed on the Processor Hierarchy node by the specification. | |
| // Note: The length field is 8 bit wide while the number of private | |
| // resource field is 32 bit wide. | |
| Length = GetProcHierarchyNodeSize (ProcInfoNode); | |
| if (Length > MAX_UINT8) { | |
| Status = EFI_INVALID_PARAMETER; | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: PPTT: Too many private resources. Count = %d. " \ | |
| "Maximum supported Processor Node size exceeded. " \ | |
| "Token = %p. Status = %r\n", | |
| ProcInfoNode->NoOfPrivateResources, | |
| ProcInfoNode->ParentToken, | |
| Status | |
| )); | |
| return Status; | |
| } | |
| // Populate the node header | |
| ProcStruct->Type = EFI_ACPI_6_4_PPTT_TYPE_PROCESSOR; | |
| ProcStruct->Length = (UINT8)Length; | |
| ProcStruct->Reserved[0] = EFI_ACPI_RESERVED_BYTE; | |
| ProcStruct->Reserved[1] = EFI_ACPI_RESERVED_BYTE; | |
| // Populate the flags | |
| ProcStruct->Flags.PhysicalPackage = ProcInfoNode->Flags & BIT0; | |
| ProcStruct->Flags.AcpiProcessorIdValid = (ProcInfoNode->Flags & BIT1) >> 1; | |
| ProcStruct->Flags.ProcessorIsAThread = (ProcInfoNode->Flags & BIT2) >> 2; | |
| ProcStruct->Flags.NodeIsALeaf = (ProcInfoNode->Flags & BIT3) >> 3; | |
| ProcStruct->Flags.IdenticalImplementation = | |
| (ProcInfoNode->Flags & BIT4) >> 4; | |
| ProcStruct->Flags.Reserved = 0; | |
| // Populate the parent reference | |
| if (ProcInfoNode->ParentToken == CM_NULL_TOKEN) { | |
| ProcStruct->Parent = 0; | |
| } else { | |
| Status = GetPpttNodeReferencedByToken ( | |
| Generator->ProcHierarchyNodeIndexedList, | |
| Generator->ProcHierarchyNodeCount, | |
| ProcInfoNode->ParentToken, | |
| &PpttNodeFound | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: PPTT: Failed to get parent processor hierarchy node " \ | |
| "reference. ParentToken = %p. ChildToken = %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_ARCH_COMMON_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->AcpiIdObjectToken == CM_NULL_TOKEN) { | |
| if (IS_PROC_NODE_LEAF (ProcInfoNode)) { | |
| Status = EFI_INVALID_PARAMETER; | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: PPTT: The 'ACPI Processor ID valid' flag is set but no " \ | |
| "ACPI ID Reference object token was provided. " \ | |
| "AcpiIdObjectToken = %p. RequestorToken = %p. Status = %r\n", | |
| ProcInfoNode->AcpiIdObjectToken, | |
| ProcInfoNode->Token, | |
| Status | |
| )); | |
| return Status; | |
| } else if (!SsdtCpuTopoPresent) { | |
| Status = EFI_INVALID_PARAMETER; | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: PPTT: The 'ACPI Processor ID valid' flag is for a non-leaf node, " \ | |
| "but no SSDT CPU TOPOLOGY table will be installed, " \ | |
| "Token = %p. Status = %r\n", | |
| ProcInfoNode->Token, | |
| Status | |
| )); | |
| return Status; | |
| } | |
| // Node is a ProcContainer with a valid ID | |
| if (ProcInfoNode->OverrideNameUidEnabled) { | |
| ProcStruct->AcpiProcessorId = ProcInfoNode->OverrideUid; | |
| } else { | |
| MetadataUid.EisaId = 0; | |
| AsciiStrCpyS (MetadataUid.NameId, METADATA_UID_NAMEID_SIZE, "ACPI0010"); | |
| Status = MetadataHandlerGenerate ( | |
| GetMetadataRoot (), | |
| MetadataTypeUid, | |
| (CM_OBJECT_TOKEN)ProcInfoNode->Token, | |
| NULL, | |
| &MetadataUid, | |
| sizeof (METADATA_OBJ_UID) | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT_EFI_ERROR (Status); | |
| return Status; | |
| } | |
| ProcStruct->AcpiProcessorId = MetadataUid.Uid; | |
| } | |
| } else { | |
| Status = GetEArmObjGicCInfo ( | |
| CfgMgrProtocol, | |
| ProcInfoNode->AcpiIdObjectToken, | |
| &GicCInfoList, | |
| &GicCInfoCount | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: PPTT: Failed to get ACPI ID Reference object token. " \ | |
| "ACPI Processor ID can't be populated. " \ | |
| "AcpiIdObjectToken = %p. RequestorToken = %p. Status = %r\n", | |
| ProcInfoNode->AcpiIdObjectToken, | |
| 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. AcpiIdObjectToken = %p. RequestorToken = %p " \ | |
| "Status = %r\n", | |
| GicCInfoCount, | |
| ProcInfoNode->AcpiIdObjectToken, | |
| 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_4_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_4_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; | |
| } | |
| /** | |
| Test whether CacheId is unique among the CacheIdList. | |
| @param [in] CacheId Cache ID to check. | |
| @param [in] CacheIdList List of already existing cache IDs. | |
| @param [in] CacheIdListSize Size of CacheIdList. | |
| @retval TRUE CacheId does not exist in CacheIdList. | |
| @retval FALSE CacheId already exists in CacheIdList. | |
| **/ | |
| STATIC | |
| BOOLEAN | |
| IsCacheIdUnique ( | |
| IN CONST UINT32 CacheId, | |
| IN CONST UINT32 *CacheIdList, | |
| IN CONST UINT32 CacheIdListSize | |
| ) | |
| { | |
| UINT32 Index; | |
| for (Index = 0; Index < CacheIdListSize; Index++) { | |
| if (CacheIdList[Index] == CacheId) { | |
| return FALSE; | |
| } | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| 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. | |
| @param [in] Revision Revision of the PPTT table being requested. | |
| @retval EFI_SUCCESS Structures updated successfully. | |
| @retval EFI_INVALID_PARAMETER A parameter is invalid. | |
| @retval EFI_NOT_FOUND A required object was not found. | |
| @retval EFI_OUT_OF_RESOURCES Out of resources. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| AddCacheTypeStructures ( | |
| IN CONST ACPI_PPTT_GENERATOR *CONST Generator, | |
| IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol, | |
| IN CONST EFI_ACPI_6_4_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER *Pptt, | |
| IN CONST UINT32 NodesStartOffset, | |
| IN CONST UINT32 Revision | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_ACPI_6_4_PPTT_STRUCTURE_CACHE *CacheStruct; | |
| PPTT_NODE_INDEXER *PpttNodeFound; | |
| CM_ARCH_COMMON_CACHE_INFO *CacheInfoNode; | |
| PPTT_NODE_INDEXER *CacheNodeIterator; | |
| UINT32 NodeCount; | |
| BOOLEAN CacheIdUnique; | |
| UINT32 NodeIndex; | |
| UINT32 *FoundCacheIds; | |
| ASSERT ( | |
| (Generator != NULL) && | |
| (CfgMgrProtocol != NULL) && | |
| (Pptt != NULL) | |
| ); | |
| CacheStruct = (EFI_ACPI_6_4_PPTT_STRUCTURE_CACHE *)((UINT8 *)Pptt + | |
| NodesStartOffset); | |
| CacheNodeIterator = Generator->CacheStructIndexedList; | |
| NodeCount = Generator->CacheStructCount; | |
| FoundCacheIds = AllocateZeroPool (NodeCount * sizeof (*FoundCacheIds)); | |
| if (FoundCacheIds == NULL) { | |
| DEBUG ((DEBUG_ERROR, "ERROR: PPTT: Failed to allocate resources.\n")); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| for (NodeIndex = 0; NodeIndex < NodeCount; NodeIndex++) { | |
| CacheInfoNode = (CM_ARCH_COMMON_CACHE_INFO *)CacheNodeIterator->Object; | |
| // Populate the node header | |
| CacheStruct->Type = EFI_ACPI_6_4_PPTT_TYPE_CACHE; | |
| CacheStruct->Length = sizeof (EFI_ACPI_6_4_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.4, 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.CacheIdValid = 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 | |
| )); | |
| goto cleanup; | |
| } | |
| // 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 | |
| )); | |
| goto cleanup; | |
| } | |
| 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 | |
| )); | |
| goto cleanup; | |
| } | |
| // 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 | |
| )); | |
| goto cleanup; | |
| } | |
| 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 | |
| )); | |
| goto cleanup; | |
| } | |
| 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 | |
| )); | |
| goto cleanup; | |
| } | |
| CacheStruct->LineSize = CacheInfoNode->LineSize; | |
| if (Revision >= 3) { | |
| // Validate and populate cache id | |
| if (CacheInfoNode->CacheId == 0) { | |
| Status = EFI_INVALID_PARAMETER; | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: PPTT: The cache id cannot be zero. Status = %r\n", | |
| Status | |
| )); | |
| goto cleanup; | |
| } | |
| CacheIdUnique = IsCacheIdUnique ( | |
| CacheInfoNode->CacheId, | |
| FoundCacheIds, | |
| NodeIndex | |
| ); | |
| if (!CacheIdUnique) { | |
| Status = EFI_INVALID_PARAMETER; | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: PPTT: The cache id is not unique. " \ | |
| "CacheId = %d. Status = %r\n", | |
| CacheInfoNode->CacheId, | |
| Status | |
| )); | |
| goto cleanup; | |
| } | |
| // Store the cache id so we can check future cache ids for uniqueness | |
| FoundCacheIds[NodeIndex] = CacheInfoNode->CacheId; | |
| CacheStruct->CacheId = CacheInfoNode->CacheId; | |
| } | |
| // Next Cache Type Structure | |
| CacheStruct = (EFI_ACPI_6_4_PPTT_STRUCTURE_CACHE *)((UINT8 *)CacheStruct + | |
| CacheStruct->Length); | |
| CacheNodeIterator++; | |
| } // for Cache Type Structure | |
| Status = EFI_SUCCESS; | |
| cleanup: | |
| FreePool (FoundCacheIds); | |
| return Status; | |
| } | |
| /** | |
| Construct the PPTT ACPI table. | |
| This function invokes the Configuration Manager protocol interface | |
| to get the required hardware information for generating the ACPI | |
| table. | |
| If this function allocates any resources then they must be freed | |
| in the FreeXXXXTableResources function. | |
| @param [in] This Pointer to the table generator. | |
| @param [in] AcpiTableInfo Pointer to the ACPI table generator to be used. | |
| @param [in] CfgMgrProtocol Pointer to the Configuration Manager | |
| Protocol Interface. | |
| @param [out] Table Pointer to the constructed ACPI Table. | |
| @retval EFI_SUCCESS Table generated successfully. | |
| @retval EFI_INVALID_PARAMETER A parameter is invalid. | |
| @retval EFI_NOT_FOUND The required object was not found. | |
| @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration | |
| Manager is less than the Object size for | |
| the requested object. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| BuildPpttTable ( | |
| IN CONST ACPI_TABLE_GENERATOR *CONST This, | |
| IN CONST CM_STD_OBJ_ACPI_TABLE_INFO *CONST AcpiTableInfo, | |
| IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol, | |
| OUT EFI_ACPI_DESCRIPTION_HEADER **CONST Table | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT32 TableSize; | |
| UINT32 ProcTopologyStructCount; | |
| UINT32 ProcHierarchyNodeCount; | |
| UINT32 CacheStructCount; | |
| UINT32 ProcHierarchyNodeOffset; | |
| UINT32 CacheStructOffset; | |
| CM_ARCH_COMMON_PROC_HIERARCHY_INFO *ProcHierarchyNodeList; | |
| CM_ARCH_COMMON_CACHE_INFO *CacheStructList; | |
| ACPI_PPTT_GENERATOR *Generator; | |
| // Pointer to the Node Indexer array | |
| PPTT_NODE_INDEXER *NodeIndexer; | |
| EFI_ACPI_6_4_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 = GetEArchCommonObjProcHierarchyInfo ( | |
| CfgMgrProtocol, | |
| CM_NULL_TOKEN, | |
| &ProcHierarchyNodeList, | |
| &ProcHierarchyNodeCount | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: PPTT: Failed to get processor hierarchy info. Status = %r\n", | |
| Status | |
| )); | |
| goto error_handler; | |
| } | |
| ProcTopologyStructCount = ProcHierarchyNodeCount; | |
| Generator->ProcHierarchyNodeCount = ProcHierarchyNodeCount; | |
| // Get the cache info and update the processor topology structure count with | |
| // Cache Type Structures (Type 1) | |
| Status = GetEArchCommonObjCacheInfo ( | |
| CfgMgrProtocol, | |
| CM_NULL_TOKEN, | |
| &CacheStructList, | |
| &CacheStructCount | |
| ); | |
| if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: PPTT: Failed to get cache info. Status = %r\n", | |
| Status | |
| )); | |
| goto error_handler; | |
| } | |
| ProcTopologyStructCount += CacheStructCount; | |
| Generator->CacheStructCount = CacheStructCount; | |
| // 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_4_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER); | |
| // Include the size of Processor Hierarchy Nodes and index them | |
| if (Generator->ProcHierarchyNodeCount != 0) { | |
| ProcHierarchyNodeOffset = TableSize; | |
| Generator->ProcHierarchyNodeIndexedList = NodeIndexer; | |
| TableSize += GetSizeofProcHierarchyNodes ( | |
| ProcHierarchyNodeOffset, | |
| ProcHierarchyNodeList, | |
| Generator->ProcHierarchyNodeCount, | |
| &NodeIndexer | |
| ); | |
| DEBUG (( | |
| DEBUG_INFO, | |
| " ProcHierarchyNodeCount = %d\n" \ | |
| " ProcHierarchyNodeOffset = 0x%x\n" \ | |
| " ProcHierarchyNodeIndexedList = 0x%p\n", | |
| Generator->ProcHierarchyNodeCount, | |
| ProcHierarchyNodeOffset, | |
| Generator->ProcHierarchyNodeIndexedList | |
| )); | |
| } | |
| // Include the size of Cache Type Structures and index them | |
| if (Generator->CacheStructCount != 0) { | |
| CacheStructOffset = TableSize; | |
| Generator->CacheStructIndexedList = NodeIndexer; | |
| TableSize += GetSizeofCacheTypeStructs ( | |
| CacheStructOffset, | |
| CacheStructList, | |
| Generator->CacheStructCount, | |
| &NodeIndexer | |
| ); | |
| DEBUG (( | |
| DEBUG_INFO, | |
| " CacheStructCount = %d\n" \ | |
| " CacheStructOffset = 0x%x\n" \ | |
| " CacheStructIndexedList = 0x%p\n", | |
| Generator->CacheStructCount, | |
| CacheStructOffset, | |
| Generator->CacheStructIndexedList | |
| )); | |
| } | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "INFO: PPTT:\n" \ | |
| " ProcTopologyStructCount = %d\n" \ | |
| " TableSize = %d\n", | |
| ProcTopologyStructCount, | |
| TableSize | |
| )); | |
| // Allocate the Buffer for the PPTT table | |
| *Table = (EFI_ACPI_DESCRIPTION_HEADER *)AllocateZeroPool (TableSize); | |
| if (*Table == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: PPTT: Failed to allocate memory for PPTT Table. " \ | |
| "Size = %d. Status = %r\n", | |
| TableSize, | |
| Status | |
| )); | |
| goto error_handler; | |
| } | |
| Pptt = (EFI_ACPI_6_4_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, | |
| AcpiTableInfo->AcpiTableRevision | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ERROR: PPTT: Failed to add Cache 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 | |
| EFIAPI | |
| 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_4_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_STRUCTURE_SIGNATURE, | |
| // ACPI Table Revision supported by this Generator | |
| EFI_ACPI_6_4_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, | |
| // Creator Revision | |
| PPTT_GENERATOR_REVISION, | |
| // Build Table function | |
| BuildPpttTable, | |
| // Free Resource function | |
| FreePpttTableResources, | |
| // Extended build function not needed | |
| NULL, | |
| // Extended build function not implemented by the generator. | |
| // Hence extended free resource function is not required. | |
| NULL | |
| }, | |
| // PPTT Generator private data | |
| // Processor topology node count | |
| 0, | |
| // Count of Processor Hierarchy Nodes | |
| 0, | |
| // Count of Cache Structures | |
| 0, | |
| // Pointer to PPTT Node Indexer | |
| NULL | |
| }; | |
| /** | |
| Register the Generator with the ACPI Table Factory. | |
| @param [in] ImageHandle The handle to the image. | |
| @param [in] SystemTable Pointer to the System Table. | |
| @retval EFI_SUCCESS The Generator is registered. | |
| @retval EFI_INVALID_PARAMETER A parameter is invalid. | |
| @retval EFI_ALREADY_STARTED The Generator for the Table ID | |
| is already registered. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AcpiPpttLibConstructor ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = RegisterAcpiTableGenerator (&PpttGenerator.Header); | |
| DEBUG ((DEBUG_INFO, "PPTT: Register Generator. Status = %r\n", Status)); | |
| ASSERT_EFI_ERROR (Status); | |
| return Status; | |
| } | |
| /** | |
| Deregister the Generator from the ACPI Table Factory. | |
| @param [in] ImageHandle The handle to the image. | |
| @param [in] SystemTable Pointer to the System Table. | |
| @retval EFI_SUCCESS The Generator is deregistered. | |
| @retval EFI_INVALID_PARAMETER A parameter is invalid. | |
| @retval EFI_NOT_FOUND The Generator is not registered. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AcpiPpttLibDestructor ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = DeregisterAcpiTableGenerator (&PpttGenerator.Header); | |
| DEBUG ((DEBUG_INFO, "PPTT: Deregister Generator. Status = %r\n", Status)); | |
| ASSERT_EFI_ERROR (Status); | |
| return Status; | |
| } |