/** @file | |
Provides cache info for each package, core type, cache level and cache type. | |
Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "InternalCpuCacheInfoLib.h" | |
/** | |
Print CpuCacheInfo array. | |
@param[in] CpuCacheInfo Pointer to the CpuCacheInfo array. | |
@param[in] CpuCacheInfoCount The length of CpuCacheInfo array. | |
**/ | |
VOID | |
CpuCacheInfoPrintCpuCacheInfoTable ( | |
IN CPU_CACHE_INFO *CpuCacheInfo, | |
IN UINTN CpuCacheInfoCount | |
) | |
{ | |
UINTN Index; | |
DEBUG ((DEBUG_INFO, "+-------+--------------------------------------------------------------------------------------+\n")); | |
DEBUG ((DEBUG_INFO, "| Index | Packge CoreType CacheLevel CacheType CacheWays (FA|DM) CacheSizeinKB CacheCount |\n")); | |
DEBUG ((DEBUG_INFO, "+-------+--------------------------------------------------------------------------------------+\n")); | |
for (Index = 0; Index < CpuCacheInfoCount; Index++) { | |
DEBUG (( | |
DEBUG_INFO, | |
"| %4x | %4x %2x %2x %2x %4x ( %x| %x) %8x %4x |\n", | |
Index, | |
CpuCacheInfo[Index].Package, | |
CpuCacheInfo[Index].CoreType, | |
CpuCacheInfo[Index].CacheLevel, | |
CpuCacheInfo[Index].CacheType, | |
CpuCacheInfo[Index].CacheWays, | |
CpuCacheInfo[Index].FullyAssociativeCache, | |
CpuCacheInfo[Index].DirectMappedCache, | |
CpuCacheInfo[Index].CacheSizeinKB, | |
CpuCacheInfo[Index].CacheCount | |
)); | |
} | |
DEBUG ((DEBUG_INFO, "+-------+--------------------------------------------------------------------------------------+\n")); | |
} | |
/** | |
Function to compare CPU package ID, core type, cache level and cache type for use in QuickSort. | |
@param[in] Buffer1 pointer to CPU_CACHE_INFO poiner to compare | |
@param[in] Buffer2 pointer to second CPU_CACHE_INFO pointer to compare | |
@retval 0 Buffer1 equal to Buffer2 | |
@retval 1 Buffer1 is greater than Buffer2 | |
@retval -1 Buffer1 is less than Buffer2 | |
**/ | |
INTN | |
EFIAPI | |
CpuCacheInfoCompare ( | |
IN CONST VOID *Buffer1, | |
IN CONST VOID *Buffer2 | |
) | |
{ | |
CPU_CACHE_INFO_COMPARATOR Comparator1, Comparator2; | |
ZeroMem (&Comparator1, sizeof (Comparator1)); | |
ZeroMem (&Comparator2, sizeof (Comparator2)); | |
Comparator1.Bits.Package = ((CPU_CACHE_INFO *)Buffer1)->Package; | |
Comparator1.Bits.CoreType = ((CPU_CACHE_INFO *)Buffer1)->CoreType; | |
Comparator1.Bits.CacheLevel = ((CPU_CACHE_INFO *)Buffer1)->CacheLevel; | |
Comparator1.Bits.CacheType = ((CPU_CACHE_INFO *)Buffer1)->CacheType; | |
Comparator2.Bits.Package = ((CPU_CACHE_INFO *)Buffer2)->Package; | |
Comparator2.Bits.CoreType = ((CPU_CACHE_INFO *)Buffer2)->CoreType; | |
Comparator2.Bits.CacheLevel = ((CPU_CACHE_INFO *)Buffer2)->CacheLevel; | |
Comparator2.Bits.CacheType = ((CPU_CACHE_INFO *)Buffer2)->CacheType; | |
if (Comparator1.Uint64 == Comparator2.Uint64) { | |
return 0; | |
} else if (Comparator1.Uint64 > Comparator2.Uint64) { | |
return 1; | |
} else { | |
return -1; | |
} | |
} | |
/** | |
Get the total number of package and package ID in the platform. | |
@param[in] ProcessorInfo Pointer to the ProcessorInfo array. | |
@param[in] NumberOfProcessors Total number of logical processors in the platform. | |
@param[in, out] Package Pointer to the Package array. | |
@retval Return the total number of package and package ID in the platform. | |
**/ | |
UINT32 | |
CpuCacheInfoGetNumberOfPackages ( | |
IN CPUID_PROCESSOR_INFO *ProcessorInfo, | |
IN UINTN NumberOfProcessors, | |
IN OUT UINT32 *Package | |
) | |
{ | |
UINTN ProcessorIndex; | |
UINT32 PackageIndex; | |
UINT32 PackageCount; | |
UINT32 CurrentPackage; | |
PackageCount = 0; | |
for (ProcessorIndex = 0; ProcessorIndex < NumberOfProcessors; ProcessorIndex++) { | |
CurrentPackage = ProcessorInfo[ProcessorIndex].Package; | |
// | |
// For the package that already exists in Package array, break out the loop. | |
// | |
for (PackageIndex = 0; PackageIndex < PackageCount; PackageIndex++) { | |
if (CurrentPackage == Package[PackageIndex]) { | |
break; | |
} | |
} | |
// | |
// For the new package, save it in Package array. | |
// | |
if (PackageIndex == PackageCount) { | |
ASSERT (PackageCount < MAX_NUM_OF_PACKAGE); | |
Package[PackageCount++] = CurrentPackage; | |
} | |
} | |
return PackageCount; | |
} | |
/** | |
Get the number of CoreType of requested package. | |
@param[in] ProcessorInfo Pointer to the ProcessorInfo array. | |
@param[in] NumberOfProcessors Total number of logical processors in the platform. | |
@param[in] Package The requested package number. | |
@retval Return the number of CoreType of requested package. | |
**/ | |
UINTN | |
CpuCacheInfoGetNumberOfCoreTypePerPackage ( | |
IN CPUID_PROCESSOR_INFO *ProcessorInfo, | |
IN UINTN NumberOfProcessors, | |
IN UINTN Package | |
) | |
{ | |
UINTN ProcessorIndex; | |
// | |
// Core Type value comes from CPUID.1Ah.EAX[31:24]. | |
// So max number of core types should be MAX_UINT8. | |
// | |
UINT8 CoreType[MAX_UINT8]; | |
UINTN CoreTypeIndex; | |
UINTN CoreTypeCount; | |
UINT8 CurrentCoreType; | |
// | |
// CoreType array is empty. | |
// | |
CoreTypeCount = 0; | |
for (ProcessorIndex = 0; ProcessorIndex < NumberOfProcessors; ProcessorIndex++) { | |
CurrentCoreType = ProcessorInfo[ProcessorIndex].CoreType; | |
if (ProcessorInfo[ProcessorIndex].Package != Package) { | |
continue; | |
} | |
// | |
// For the type that already exists in CoreType array, break out the loop. | |
// | |
for (CoreTypeIndex = 0; CoreTypeIndex < CoreTypeCount; CoreTypeIndex++) { | |
if (CurrentCoreType == CoreType[CoreTypeIndex]) { | |
break; | |
} | |
} | |
// | |
// For the new type, save it in CoreType array. | |
// | |
if (CoreTypeIndex == CoreTypeCount) { | |
ASSERT (CoreTypeCount < MAX_UINT8); | |
CoreType[CoreTypeCount++] = CurrentCoreType; | |
} | |
} | |
return CoreTypeCount; | |
} | |
/** | |
Collect core and cache information of calling processor via CPUID instructions. | |
@param[in, out] Buffer The pointer to private data buffer. | |
**/ | |
VOID | |
EFIAPI | |
CpuCacheInfoCollectCoreAndCacheData ( | |
IN OUT VOID *Buffer | |
) | |
{ | |
UINTN ProcessorIndex; | |
UINT32 CpuidMaxInput; | |
UINT8 CacheParamLeafIndex; | |
CPUID_CACHE_PARAMS_EAX CacheParamEax; | |
CPUID_CACHE_PARAMS_EBX CacheParamEbx; | |
UINT32 CacheParamEcx; | |
CPUID_CACHE_PARAMS_EDX CacheParamEdx; | |
CPUID_NATIVE_MODEL_ID_AND_CORE_TYPE_EAX NativeModelIdAndCoreTypeEax; | |
COLLECT_CPUID_CACHE_DATA_CONTEXT *Context; | |
CPUID_CACHE_DATA *CacheData; | |
Context = (COLLECT_CPUID_CACHE_DATA_CONTEXT *)Buffer; | |
ProcessorIndex = CpuCacheInfoWhoAmI (Context->MpServices); | |
CacheData = &Context->CacheData[MAX_NUM_OF_CACHE_PARAMS_LEAF * ProcessorIndex]; | |
AsmCpuid (CPUID_SIGNATURE, &CpuidMaxInput, NULL, NULL, NULL); | |
// | |
// get CoreType if CPUID_HYBRID_INFORMATION leaf is supported. | |
// | |
Context->ProcessorInfo[ProcessorIndex].CoreType = 0; | |
if (CpuidMaxInput >= CPUID_HYBRID_INFORMATION) { | |
AsmCpuidEx (CPUID_HYBRID_INFORMATION, CPUID_HYBRID_INFORMATION_MAIN_LEAF, &NativeModelIdAndCoreTypeEax.Uint32, NULL, NULL, NULL); | |
Context->ProcessorInfo[ProcessorIndex].CoreType = (UINT8)NativeModelIdAndCoreTypeEax.Bits.CoreType; | |
} | |
// | |
// cache hierarchy starts with an index value of 0. | |
// | |
CacheParamLeafIndex = 0; | |
while (CacheParamLeafIndex < MAX_NUM_OF_CACHE_PARAMS_LEAF) { | |
AsmCpuidEx (CPUID_CACHE_PARAMS, CacheParamLeafIndex, &CacheParamEax.Uint32, &CacheParamEbx.Uint32, &CacheParamEcx, &CacheParamEdx.Uint32); | |
if (CacheParamEax.Bits.CacheType == 0) { | |
break; | |
} | |
CacheData[CacheParamLeafIndex].CacheLevel = (UINT8)CacheParamEax.Bits.CacheLevel; | |
CacheData[CacheParamLeafIndex].CacheType = (UINT8)CacheParamEax.Bits.CacheType; | |
CacheData[CacheParamLeafIndex].CacheWays = (UINT16)CacheParamEbx.Bits.Ways; | |
CacheData[CacheParamLeafIndex].FullyAssociativeCache = (UINT8)CacheParamEax.Bits.FullyAssociativeCache; | |
CacheData[CacheParamLeafIndex].DirectMappedCache = (UINT8)(CacheParamEdx.Bits.ComplexCacheIndexing == 0); | |
CacheData[CacheParamLeafIndex].CacheShareBits = (UINT16)CacheParamEax.Bits.MaximumAddressableIdsForLogicalProcessors; | |
CacheData[CacheParamLeafIndex].CacheSizeinKB = (CacheParamEbx.Bits.Ways + 1) * | |
(CacheParamEbx.Bits.LinePartitions + 1) * (CacheParamEbx.Bits.LineSize + 1) * (CacheParamEcx + 1) / SIZE_1KB; | |
CacheParamLeafIndex++; | |
} | |
} | |
/** | |
Collect CacheInfo data from the CacheData. | |
@param[in] CacheData Pointer to the CacheData array. | |
@param[in] ProcessorInfo Pointer to the ProcessorInfo array. | |
@param[in] NumberOfProcessors Total number of logical processors in the platform. | |
@param[in, out] CacheInfo Pointer to the CacheInfo array. | |
@param[in, out] CacheInfoCount As input, point to the length of response CacheInfo array. | |
As output, point to the actual length of response CacheInfo array. | |
@retval EFI_SUCCESS Function completed successfully. | |
@retval EFI_OUT_OF_RESOURCES Required resources could not be allocated. | |
@retval EFI_BUFFER_TOO_SMALL CacheInfoCount is too small to hold the response CacheInfo | |
array. CacheInfoCount has been updated with the length needed | |
to complete the request. | |
**/ | |
EFI_STATUS | |
CpuCacheInfoCollectCpuCacheInfoData ( | |
IN CPUID_CACHE_DATA *CacheData, | |
IN CPUID_PROCESSOR_INFO *ProcessorInfo, | |
IN UINTN NumberOfProcessors, | |
IN OUT CPU_CACHE_INFO *CacheInfo, | |
IN OUT UINTN *CacheInfoCount | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 NumberOfPackage; | |
UINT32 Package[MAX_NUM_OF_PACKAGE]; | |
UINTN PackageIndex; | |
UINTN TotalNumberOfCoreType; | |
UINTN MaxCacheInfoCount; | |
CPU_CACHE_INFO *LocalCacheInfo; | |
UINTN CacheInfoIndex; | |
UINTN LocalCacheInfoCount; | |
UINTN Index; | |
UINTN NextIndex; | |
CPU_CACHE_INFO SortBuffer; | |
// | |
// Get number of Packages and Package ID. | |
// | |
NumberOfPackage = CpuCacheInfoGetNumberOfPackages (ProcessorInfo, NumberOfProcessors, Package); | |
// | |
// Get number of core types for each package and count the total number. | |
// E.g. If Package1 and Package2 both have 2 core types, the total number is 4. | |
// | |
TotalNumberOfCoreType = 0; | |
for (PackageIndex = 0; PackageIndex < NumberOfPackage; PackageIndex++) { | |
TotalNumberOfCoreType += CpuCacheInfoGetNumberOfCoreTypePerPackage (ProcessorInfo, NumberOfProcessors, Package[PackageIndex]); | |
} | |
MaxCacheInfoCount = TotalNumberOfCoreType * MAX_NUM_OF_CACHE_PARAMS_LEAF; | |
LocalCacheInfo = AllocatePages (EFI_SIZE_TO_PAGES (MaxCacheInfoCount * sizeof (*LocalCacheInfo))); | |
ASSERT (LocalCacheInfo != NULL); | |
if (LocalCacheInfo == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
LocalCacheInfoCount = 0; | |
for (Index = 0; Index < NumberOfProcessors * MAX_NUM_OF_CACHE_PARAMS_LEAF; Index++) { | |
if (CacheData[Index].CacheSizeinKB == 0) { | |
continue; | |
} | |
// | |
// For the sharing caches, clear their CacheSize. | |
// | |
for (NextIndex = Index + 1; NextIndex < NumberOfProcessors * MAX_NUM_OF_CACHE_PARAMS_LEAF; NextIndex++) { | |
if (CacheData[NextIndex].CacheSizeinKB == 0) { | |
continue; | |
} | |
if ((CacheData[Index].CacheLevel == CacheData[NextIndex].CacheLevel) && | |
(CacheData[Index].CacheType == CacheData[NextIndex].CacheType) && | |
(ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].Package == ProcessorInfo[NextIndex / MAX_NUM_OF_CACHE_PARAMS_LEAF].Package) && | |
(ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].CoreType == ProcessorInfo[NextIndex / MAX_NUM_OF_CACHE_PARAMS_LEAF].CoreType) && | |
((ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].ApicId & ~CacheData[Index].CacheShareBits) == | |
(ProcessorInfo[NextIndex / MAX_NUM_OF_CACHE_PARAMS_LEAF].ApicId & ~CacheData[NextIndex].CacheShareBits))) | |
{ | |
CacheData[NextIndex].CacheSizeinKB = 0; // uses the sharing cache | |
} | |
} | |
// | |
// For the cache that already exists in LocalCacheInfo, increase its CacheCount. | |
// | |
for (CacheInfoIndex = 0; CacheInfoIndex < LocalCacheInfoCount; CacheInfoIndex++) { | |
if ((LocalCacheInfo[CacheInfoIndex].Package == ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].Package) && | |
(LocalCacheInfo[CacheInfoIndex].CoreType == ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].CoreType) && | |
(LocalCacheInfo[CacheInfoIndex].CacheLevel == CacheData[Index].CacheLevel) && | |
(LocalCacheInfo[CacheInfoIndex].CacheType == CacheData[Index].CacheType)) | |
{ | |
LocalCacheInfo[CacheInfoIndex].CacheCount++; | |
break; | |
} | |
} | |
// | |
// For the new cache with different Package, CoreType, CacheLevel or CacheType, copy its | |
// data into LocalCacheInfo buffer. | |
// | |
if (CacheInfoIndex == LocalCacheInfoCount) { | |
ASSERT (LocalCacheInfoCount < MaxCacheInfoCount); | |
LocalCacheInfo[LocalCacheInfoCount].Package = ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].Package; | |
LocalCacheInfo[LocalCacheInfoCount].CoreType = ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].CoreType; | |
LocalCacheInfo[LocalCacheInfoCount].CacheLevel = CacheData[Index].CacheLevel; | |
LocalCacheInfo[LocalCacheInfoCount].CacheType = CacheData[Index].CacheType; | |
LocalCacheInfo[LocalCacheInfoCount].CacheWays = CacheData[Index].CacheWays; | |
LocalCacheInfo[LocalCacheInfoCount].FullyAssociativeCache = CacheData[Index].FullyAssociativeCache; | |
LocalCacheInfo[LocalCacheInfoCount].DirectMappedCache = CacheData[Index].DirectMappedCache; | |
LocalCacheInfo[LocalCacheInfoCount].CacheSizeinKB = CacheData[Index].CacheSizeinKB; | |
LocalCacheInfo[LocalCacheInfoCount].CacheCount = 1; | |
LocalCacheInfoCount++; | |
} | |
} | |
if (*CacheInfoCount < LocalCacheInfoCount) { | |
Status = EFI_BUFFER_TOO_SMALL; | |
} else { | |
// | |
// Sort LocalCacheInfo array by CPU package ID, core type, cache level and cache type. | |
// | |
QuickSort (LocalCacheInfo, LocalCacheInfoCount, sizeof (*LocalCacheInfo), CpuCacheInfoCompare, (VOID *)&SortBuffer); | |
CopyMem (CacheInfo, LocalCacheInfo, sizeof (*CacheInfo) * LocalCacheInfoCount); | |
DEBUG_CODE ( | |
CpuCacheInfoPrintCpuCacheInfoTable (CacheInfo, LocalCacheInfoCount); | |
); | |
Status = EFI_SUCCESS; | |
} | |
*CacheInfoCount = LocalCacheInfoCount; | |
FreePages (LocalCacheInfo, EFI_SIZE_TO_PAGES (MaxCacheInfoCount * sizeof (*LocalCacheInfo))); | |
return Status; | |
} | |
/** | |
Get CpuCacheInfo data array. The array is sorted by CPU package ID, core type, cache level and cache type. | |
@param[in, out] CpuCacheInfo Pointer to the CpuCacheInfo array. | |
@param[in, out] CpuCacheInfoCount As input, point to the length of response CpuCacheInfo array. | |
As output, point to the actual length of response CpuCacheInfo array. | |
@retval EFI_SUCCESS Function completed successfully. | |
@retval EFI_INVALID_PARAMETER CpuCacheInfoCount is NULL. | |
@retval EFI_INVALID_PARAMETER CpuCacheInfo is NULL while CpuCacheInfoCount contains the value | |
greater than zero. | |
@retval EFI_UNSUPPORTED Processor does not support CPUID_CACHE_PARAMS Leaf. | |
@retval EFI_OUT_OF_RESOURCES Required resources could not be allocated. | |
@retval EFI_BUFFER_TOO_SMALL CpuCacheInfoCount is too small to hold the response CpuCacheInfo | |
array. CpuCacheInfoCount has been updated with the length needed | |
to complete the request. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GetCpuCacheInfo ( | |
IN OUT CPU_CACHE_INFO *CpuCacheInfo, | |
IN OUT UINTN *CpuCacheInfoCount | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 CpuidMaxInput; | |
UINT32 NumberOfProcessors; | |
UINTN CacheDataCount; | |
UINTN ProcessorIndex; | |
EFI_PROCESSOR_INFORMATION ProcessorInfo; | |
COLLECT_CPUID_CACHE_DATA_CONTEXT Context; | |
if (CpuCacheInfoCount == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((*CpuCacheInfoCount != 0) && (CpuCacheInfo == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
AsmCpuid (CPUID_SIGNATURE, &CpuidMaxInput, NULL, NULL, NULL); | |
if (CpuidMaxInput < CPUID_CACHE_PARAMS) { | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Initialize COLLECT_CPUID_CACHE_DATA_CONTEXT.MpServices. | |
// | |
CpuCacheInfoGetMpServices (&Context.MpServices); | |
NumberOfProcessors = CpuCacheInfoGetNumberOfProcessors (Context.MpServices); | |
// | |
// Initialize COLLECT_CPUID_CACHE_DATA_CONTEXT.ProcessorInfo. | |
// | |
Context.ProcessorInfo = AllocatePages (EFI_SIZE_TO_PAGES (NumberOfProcessors * sizeof (*Context.ProcessorInfo))); | |
ASSERT (Context.ProcessorInfo != NULL); | |
if (Context.ProcessorInfo == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Initialize COLLECT_CPUID_CACHE_DATA_CONTEXT.CacheData. | |
// CacheData array consists of CPUID_CACHE_DATA data structure for each Cpuid Cache Parameter Leaf | |
// per logical processor. The array begin with data of each Cache Parameter Leaf of processor 0, followed | |
// by data of each Cache Parameter Leaf of processor 1 ... | |
// | |
CacheDataCount = NumberOfProcessors * MAX_NUM_OF_CACHE_PARAMS_LEAF; | |
Context.CacheData = AllocatePages (EFI_SIZE_TO_PAGES (CacheDataCount * sizeof (*Context.CacheData))); | |
ASSERT (Context.CacheData != NULL); | |
if (Context.CacheData == NULL) { | |
FreePages (Context.ProcessorInfo, EFI_SIZE_TO_PAGES (NumberOfProcessors * sizeof (*Context.ProcessorInfo))); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
ZeroMem (Context.CacheData, CacheDataCount * sizeof (*Context.CacheData)); | |
// | |
// Collect Package ID and APIC ID of all processors. | |
// | |
for (ProcessorIndex = 0; ProcessorIndex < NumberOfProcessors; ProcessorIndex++) { | |
CpuCacheInfoGetProcessorInfo (Context.MpServices, ProcessorIndex, &ProcessorInfo); | |
Context.ProcessorInfo[ProcessorIndex].Package = ProcessorInfo.Location.Package; | |
Context.ProcessorInfo[ProcessorIndex].ApicId = (UINT32)ProcessorInfo.ProcessorId; | |
} | |
// | |
// Wakeup all processors for CacheData(core type and cache data) collection. | |
// | |
CpuCacheInfoStartupAllCPUs (Context.MpServices, CpuCacheInfoCollectCoreAndCacheData, &Context); | |
// | |
// Collect CpuCacheInfo data from CacheData. | |
// | |
Status = CpuCacheInfoCollectCpuCacheInfoData (Context.CacheData, Context.ProcessorInfo, NumberOfProcessors, CpuCacheInfo, CpuCacheInfoCount); | |
FreePages (Context.CacheData, EFI_SIZE_TO_PAGES (CacheDataCount * sizeof (*Context.CacheData))); | |
FreePages (Context.ProcessorInfo, EFI_SIZE_TO_PAGES (NumberOfProcessors * sizeof (*Context.ProcessorInfo))); | |
return Status; | |
} |