mirror of https://github.com/acidanthera/audk.git
1480 lines
47 KiB
C
1480 lines
47 KiB
C
/** @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/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Protocol/AcpiTable.h>
|
|
|
|
// Module specific include files.
|
|
#include <AcpiTableGenerator.h>
|
|
#include <ConfigurationManagerObject.h>
|
|
#include <ConfigurationManagerHelper.h>
|
|
#include <Library/TableHelperLib.h>
|
|
#include <Protocol/ConfigurationManagerProtocol.h>
|
|
|
|
#include "PpttGenerator.h"
|
|
|
|
/**
|
|
ARM standard PPTT Generator
|
|
|
|
Requirements:
|
|
The following Configuration Manager Object(s) are used by this Generator:
|
|
- EArmObjProcHierarchyInfo (REQUIRED)
|
|
- EArmObjCacheInfo
|
|
- 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 cross-CM-object-
|
|
reference information from the Configuration Manager.
|
|
*/
|
|
GET_OBJECT_LIST (
|
|
EObjNameSpaceArm,
|
|
EArmObjCmRef,
|
|
CM_ARM_OBJ_REF
|
|
);
|
|
|
|
/**
|
|
This macro expands to a function that retrieves the GIC CPU interface
|
|
information from the Configuration Manager.
|
|
*/
|
|
GET_OBJECT_LIST (
|
|
EObjNameSpaceArm,
|
|
EArmObjGicCInfo,
|
|
CM_ARM_GICC_INFO
|
|
);
|
|
|
|
/**
|
|
Returns the size of the PPTT Processor Hierarchy Node (Type 0) given a
|
|
Processor Hierarchy Info CM object.
|
|
|
|
@param [in] Node Pointer to Processor Hierarchy Info CM object which
|
|
represents the Processor Hierarchy Node to be generated.
|
|
|
|
@retval Size of the Processor Hierarchy Node in bytes.
|
|
**/
|
|
STATIC
|
|
UINT32
|
|
GetProcHierarchyNodeSize (
|
|
IN CONST CM_ARM_PROC_HIERARCHY_INFO *Node
|
|
)
|
|
{
|
|
ASSERT (Node != NULL);
|
|
|
|
// <size of Processor Hierarchy Node> + <size of Private Resources array>
|
|
return sizeof (EFI_ACPI_6_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_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_4_PPTT_STRUCTURE_CACHE),
|
|
CM_ARM_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_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. 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_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 IsGicCTokenDuplicated;
|
|
|
|
CM_ARM_GICC_INFO *GicCInfoList;
|
|
UINT32 GicCInfoCount;
|
|
UINT32 UniqueGicCRefCount;
|
|
|
|
PPTT_NODE_INDEXER *PpttNodeFound;
|
|
CM_ARM_PROC_HIERARCHY_INFO *ProcInfoNode;
|
|
|
|
PPTT_NODE_INDEXER *ProcNodeIterator;
|
|
UINT32 NodeCount;
|
|
UINT32 Length;
|
|
|
|
ASSERT (
|
|
(Generator != NULL) &&
|
|
(CfgMgrProtocol != NULL) &&
|
|
(Pptt != NULL)
|
|
);
|
|
|
|
ProcStruct = (EFI_ACPI_6_4_PPTT_STRUCTURE_PROCESSOR *)((UINT8 *)Pptt +
|
|
NodesStartOffset);
|
|
|
|
ProcNodeIterator = Generator->ProcHierarchyNodeIndexedList;
|
|
NodeCount = Generator->ProcHierarchyNodeCount;
|
|
|
|
// Check if every GICC Object is referenced by onlu one Proc Node
|
|
IsGicCTokenDuplicated = FindDuplicateValue (
|
|
ProcNodeIterator,
|
|
NodeCount,
|
|
sizeof (PPTT_NODE_INDEXER),
|
|
IsGicCTokenEqual
|
|
);
|
|
// Duplicate GIC CPU Interface Token was found so two PPTT Processor Hierarchy
|
|
// Nodes map to the same MADT GICC structure
|
|
if (IsGicCTokenDuplicated) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
UniqueGicCRefCount = 0;
|
|
|
|
while (NodeCount-- != 0) {
|
|
ProcInfoNode = (CM_ARM_PROC_HIERARCHY_INFO *)ProcNodeIterator->Object;
|
|
|
|
// Check if the private resource count is within the size limit
|
|
// imposed on the Processor Hierarchy node by the specification.
|
|
// Note: The length field is 8 bit wide while the number of private
|
|
// resource field is 32 bit wide.
|
|
Length = GetProcHierarchyNodeSize (ProcInfoNode);
|
|
if (Length > MAX_UINT8) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"ERROR: PPTT: Too many private resources. Count = %d. " \
|
|
"Maximum supported Processor Node size exceeded. " \
|
|
"Token = %p. Status = %r\n",
|
|
ProcInfoNode->NoOfPrivateResources,
|
|
ProcInfoNode->ParentToken,
|
|
Status
|
|
));
|
|
return Status;
|
|
}
|
|
|
|
// Populate the node header
|
|
ProcStruct->Type = EFI_ACPI_6_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. 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_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_ARM_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_ARM_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_ARM_PROC_HIERARCHY_INFO *ProcHierarchyNodeList;
|
|
CM_ARM_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 = GetEArmObjProcHierarchyInfo (
|
|
CfgMgrProtocol,
|
|
CM_NULL_TOKEN,
|
|
&ProcHierarchyNodeList,
|
|
&ProcHierarchyNodeCount
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"ERROR: PPTT: Failed to get processor hierarchy info. Status = %r\n",
|
|
Status
|
|
));
|
|
goto error_handler;
|
|
}
|
|
|
|
ProcTopologyStructCount = ProcHierarchyNodeCount;
|
|
Generator->ProcHierarchyNodeCount = ProcHierarchyNodeCount;
|
|
|
|
// Get the cache info and update the processor topology structure count with
|
|
// Cache Type Structures (Type 1)
|
|
Status = GetEArmObjCacheInfo (
|
|
CfgMgrProtocol,
|
|
CM_NULL_TOKEN,
|
|
&CacheStructList,
|
|
&CacheStructCount
|
|
);
|
|
if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"ERROR: PPTT: Failed to get cache info. Status = %r\n",
|
|
Status
|
|
));
|
|
goto error_handler;
|
|
}
|
|
|
|
ProcTopologyStructCount += CacheStructCount;
|
|
Generator->CacheStructCount = CacheStructCount;
|
|
|
|
// 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
|
|
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_ARM,
|
|
// Creator Revision
|
|
PPTT_GENERATOR_REVISION,
|
|
// Build Table function
|
|
BuildPpttTable,
|
|
// Free Resource function
|
|
FreePpttTableResources,
|
|
// Extended build function not needed
|
|
NULL,
|
|
// Extended build function not implemented by the generator.
|
|
// Hence extended free resource function is not required.
|
|
NULL
|
|
},
|
|
|
|
// PPTT Generator private data
|
|
|
|
// Processor topology node count
|
|
0,
|
|
// Count of Processor Hierarchy Nodes
|
|
0,
|
|
// Count of Cache Structures
|
|
0,
|
|
// 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;
|
|
}
|