blob: c7973735e15d41ed9dcd71c1809a8ce0e3dcb54f [file] [log] [blame]
/** @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;
}