blob: 4fd58e45a9c0a24d77addaf7a6cfa18c4c24f8a2 [file]
/** @file
X64 specific ACPI Maximum System Characteristics Table (MSCT) Generator
Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
// Module specific include files.
#include <AcpiTableGenerator.h>
#include <ConfigurationManagerObject.h>
#include <ConfigurationManagerHelper.h>
#include <Library/TableHelperLib.h>
#include <Protocol/ConfigurationManagerProtocol.h>
#include <Library/CmObjHelperLib.h>
#include "MsctGenerator.h"
/**
This macro is used to get the object information for the Local APIC X2APIC
Affinity object.
*/
GET_OBJECT_LIST (
EObjNameSpaceX64,
EX64ObjLocalApicX2ApicAffinityInfo,
CM_X64_LOCAL_APIC_X2APIC_AFFINITY_INFO
);
/** Get architecture processor domain information.
@param [in] CfgMgrProtocol Pointer to the Configuration Manager
Protocol.
@param [out] ProcDomainInfo Pointer to the processor domain information.
@param [out] ProcDomainInfoCount Pointer to the count of processor domain
information structures.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_INVALID_PARAMETER A parameter is invalid.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval Other Other EFI_STATUS error from called functions.
**/
EFI_STATUS
EFIAPI
GetArchProcessorDomainInfo (
IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
OUT UINT32 **ProcDomainInfo,
OUT UINT32 *ProcDomainInfoCount
)
{
CM_X64_LOCAL_APIC_X2APIC_AFFINITY_INFO *CmProcDomain;
EFI_STATUS Status;
UINT32 Index;
UINT32 ProcDomainCount;
UINT32 *ProcDomain;
if ((CfgMgrProtocol == NULL) ||
(ProcDomainInfo == NULL) ||
(ProcDomainInfoCount == NULL))
{
return EFI_INVALID_PARAMETER;
}
CmProcDomain = NULL;
ProcDomain = NULL;
ProcDomainCount = 0;
Status = GetEX64ObjLocalApicX2ApicAffinityInfo (
CfgMgrProtocol,
CM_NULL_TOKEN,
&CmProcDomain,
&ProcDomainCount
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"MSCT: Failed to get processor affinity information. Status = %r\n",
Status
));
goto error_handler;
}
if ((ProcDomainCount == 0) || (CmProcDomain == NULL)) {
DEBUG ((
DEBUG_ERROR,
"MSCT: No Local Apic/X2Apic Affinity information found.\n"
));
Status = EFI_NOT_FOUND;
goto error_handler;
}
ProcDomain = AllocateZeroPool (
sizeof (UINT32) *
ProcDomainCount
);
if (ProcDomain == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto error_handler;
}
for (Index = 0; Index < ProcDomainCount; Index++) {
Status = GetProximityDomainId (
CfgMgrProtocol,
CmProcDomain[Index].ProximityDomain,
CmProcDomain[Index].ProximityDomainToken,
&ProcDomain[Index]
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"MSCT: Failed to get Proximity Domain ID for index %d. Status = %r\n",
Index,
Status
));
goto error_handler;
}
}
*ProcDomainInfo = ProcDomain;
*ProcDomainInfoCount = ProcDomainCount;
return EFI_SUCCESS;
error_handler:
if (ProcDomain != NULL) {
FreePool (ProcDomain);
}
return Status;
}
/** Get the count of unique architecture clock domains.
Enumerates all Local APIC/X2APIC affinity entries and returns the number of
unique clock proximity domain IDs in the system.
@param [in] CfgMgrProtocol Pointer to the Configuration Manager
Protocol.
@param [out] ClockDomainCount On success, the cardinal count of unique
clock domains.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_INVALID_PARAMETER A parameter is invalid.
@retval EFI_NOT_FOUND No affinity information was found.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval Other Other EFI_STATUS error from called functions.
**/
EFI_STATUS
EFIAPI
GetArchClockDomainInfo (
IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
OUT UINT32 *ClockDomainCount
)
{
EFI_STATUS Status;
UINT32 ProximityDomain;
UINT32 CmCount;
UINT32 Index;
UINT32 UniqueIndex;
UINT32 UniqueCount;
UINT32 *ClockDomains;
BOOLEAN IsUnique;
CM_X64_LOCAL_APIC_X2APIC_AFFINITY_INFO *CmX2ApicAffinity;
if ((CfgMgrProtocol == NULL) ||
(ClockDomainCount == NULL))
{
return EFI_INVALID_PARAMETER;
}
CmX2ApicAffinity = NULL;
ClockDomains = NULL;
UniqueCount = 0;
CmCount = 0;
Status = GetEX64ObjLocalApicX2ApicAffinityInfo (
CfgMgrProtocol,
CM_NULL_TOKEN,
&CmX2ApicAffinity,
&CmCount
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"ERROR: MSCT: Failed to get Local Apic/X2Apic Affinity Info. Status = %r\n",
Status
));
return Status;
}
if ((CmX2ApicAffinity == NULL) || (CmCount == 0)) {
DEBUG ((
DEBUG_ERROR,
"ERROR: MSCT: Local Apic/X2Apic Affinity information not provided.\n"
));
return EFI_NOT_FOUND;
}
// Allocate worst-case storage: every CPU could be in its own clock domain.
ClockDomains = AllocateZeroPool (sizeof (UINT32) * CmCount);
if (ClockDomains == NULL) {
Status = EFI_OUT_OF_RESOURCES;
ASSERT_EFI_ERROR (Status);
return Status;
}
for (Index = 0; Index < CmCount; Index++) {
Status = GetProximityDomainId (
CfgMgrProtocol,
CmX2ApicAffinity[Index].ClockDomain,
CmX2ApicAffinity[Index].ClockDomainToken,
&ProximityDomain
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"ERROR: MSCT: Failed to get Clock Domain ID for index %d. Status = %r\n",
Index,
Status
));
ASSERT_EFI_ERROR (Status);
goto error_handler;
}
// Record the proximity domain ID only if not already seen.
IsUnique = TRUE;
for (UniqueIndex = 0; UniqueIndex < UniqueCount; UniqueIndex++) {
if (ClockDomains[UniqueIndex] == ProximityDomain) {
IsUnique = FALSE;
break;
}
}
if (IsUnique) {
ClockDomains[UniqueCount++] = ProximityDomain;
}
}
*ClockDomainCount = UniqueCount;
Status = EFI_SUCCESS;
error_handler:
if (ClockDomains != NULL) {
FreePool (ClockDomains);
}
return Status;
}