blob: 552fdab4177d3fe33f1cba37d40447e5d907ffa3 [file] [log] [blame]
/** @file
CPU Features Initialize functions.
Copyright (c) 2017 - 2023, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "RegisterCpuFeatures.h"
CHAR16 *mDependTypeStr[] = { L"None", L"Thread", L"Core", L"Package", L"Invalid" };
/**
Worker function to save PcdCpuFeaturesCapability.
@param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer
@param[in] BitMaskSize CPU feature bits mask buffer size.
**/
VOID
SetCapabilityPcd (
IN UINT8 *SupportedFeatureMask,
IN UINTN BitMaskSize
)
{
EFI_STATUS Status;
Status = PcdSetPtrS (PcdCpuFeaturesCapability, &BitMaskSize, SupportedFeatureMask);
ASSERT_EFI_ERROR (Status);
}
/**
Worker function to save PcdCpuFeaturesSetting.
@param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer
@param[in] BitMaskSize CPU feature bits mask buffer size.
**/
VOID
SetSettingPcd (
IN UINT8 *SupportedFeatureMask,
IN UINTN BitMaskSize
)
{
EFI_STATUS Status;
Status = PcdSetPtrS (PcdCpuFeaturesSetting, &BitMaskSize, SupportedFeatureMask);
ASSERT_EFI_ERROR (Status);
}
/**
Collects CPU type and feature information.
@param[in, out] CpuInfo The pointer to CPU feature information
**/
VOID
FillProcessorInfo (
IN OUT REGISTER_CPU_FEATURE_INFORMATION *CpuInfo
)
{
CPUID_VERSION_INFO_EAX Eax;
CPUID_VERSION_INFO_ECX Ecx;
CPUID_VERSION_INFO_EDX Edx;
UINT32 DisplayedFamily;
UINT32 DisplayedModel;
AsmCpuid (CPUID_VERSION_INFO, &Eax.Uint32, NULL, &Ecx.Uint32, &Edx.Uint32);
DisplayedFamily = Eax.Bits.FamilyId;
if (Eax.Bits.FamilyId == 0x0F) {
DisplayedFamily += Eax.Bits.ExtendedFamilyId;
}
DisplayedModel = Eax.Bits.Model;
if ((Eax.Bits.FamilyId == 0x06) || (Eax.Bits.FamilyId == 0x0f)) {
DisplayedModel += (Eax.Bits.ExtendedModelId << 4);
}
CpuInfo->DisplayFamily = DisplayedFamily;
CpuInfo->DisplayModel = DisplayedModel;
CpuInfo->SteppingId = Eax.Bits.SteppingId;
CpuInfo->ProcessorType = Eax.Bits.ProcessorType;
CpuInfo->CpuIdVersionInfoEcx.Uint32 = Ecx.Uint32;
CpuInfo->CpuIdVersionInfoEdx.Uint32 = Edx.Uint32;
}
/**
Prepares for private data used for CPU features.
**/
VOID
CpuInitDataInitialize (
VOID
)
{
EFI_STATUS Status;
UINTN ProcessorNumber;
EFI_PROCESSOR_INFORMATION ProcessorInfoBuffer;
CPU_FEATURES_ENTRY *CpuFeature;
CPU_FEATURES_INIT_ORDER *InitOrder;
CPU_FEATURES_DATA *CpuFeaturesData;
LIST_ENTRY *Entry;
UINT32 Core;
UINT32 Package;
UINT32 Thread;
EFI_CPU_PHYSICAL_LOCATION *Location;
UINT32 PackageIndex;
UINT32 CoreIndex;
UINTN Pages;
UINT32 FirstPackage;
UINT32 *FirstCore;
UINT32 *FirstThread;
ACPI_CPU_DATA *AcpiCpuData;
CPU_STATUS_INFORMATION *CpuStatus;
UINT32 *ThreadCountPerPackage;
UINT8 *ThreadCountPerCore;
UINTN NumberOfCpus;
UINTN NumberOfEnabledProcessors;
Core = 0;
Package = 0;
Thread = 0;
CpuFeaturesData = GetCpuFeaturesData ();
//
// Initialize CpuFeaturesData->MpService as early as possile, so later function can use it.
//
CpuFeaturesData->MpService = GetMpService ();
GetNumberOfProcessor (&NumberOfCpus, &NumberOfEnabledProcessors);
CpuFeaturesData->InitOrder = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (CPU_FEATURES_INIT_ORDER) * NumberOfCpus));
ASSERT (CpuFeaturesData->InitOrder != NULL);
ZeroMem (CpuFeaturesData->InitOrder, sizeof (CPU_FEATURES_INIT_ORDER) * NumberOfCpus);
//
// Collect CPU Features information
//
Entry = GetFirstNode (&CpuFeaturesData->FeatureList);
while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) {
CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (Entry);
ASSERT (CpuFeature->InitializeFunc != NULL);
if (CpuFeature->GetConfigDataFunc != NULL) {
CpuFeature->ConfigData = CpuFeature->GetConfigDataFunc (NumberOfCpus);
}
Entry = Entry->ForwardLink;
}
CpuFeaturesData->NumberOfCpus = (UINT32)NumberOfCpus;
AcpiCpuData = GetAcpiCpuData ();
ASSERT (AcpiCpuData != NULL);
CpuFeaturesData->AcpiCpuData = AcpiCpuData;
CpuStatus = &AcpiCpuData->CpuFeatureInitData.CpuStatus;
Location = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (EFI_CPU_PHYSICAL_LOCATION) * NumberOfCpus));
ASSERT (Location != NULL);
ZeroMem (Location, sizeof (EFI_CPU_PHYSICAL_LOCATION) * NumberOfCpus);
AcpiCpuData->CpuFeatureInitData.ApLocation = (EFI_PHYSICAL_ADDRESS)(UINTN)Location;
for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) {
InitOrder = &CpuFeaturesData->InitOrder[ProcessorNumber];
InitOrder->FeaturesSupportedMask = AllocateZeroPool (CpuFeaturesData->BitMaskSize);
ASSERT (InitOrder->FeaturesSupportedMask != NULL);
InitializeListHead (&InitOrder->OrderList);
Status = GetProcessorInformation (ProcessorNumber, &ProcessorInfoBuffer);
ASSERT_EFI_ERROR (Status);
CopyMem (
&InitOrder->CpuInfo.ProcessorInfo,
&ProcessorInfoBuffer,
sizeof (EFI_PROCESSOR_INFORMATION)
);
CopyMem (
&Location[ProcessorNumber],
&ProcessorInfoBuffer.Location,
sizeof (EFI_CPU_PHYSICAL_LOCATION)
);
//
// Collect CPU package count info.
//
if (Package < ProcessorInfoBuffer.Location.Package) {
Package = ProcessorInfoBuffer.Location.Package;
}
//
// Collect CPU max core count info.
//
if (Core < ProcessorInfoBuffer.Location.Core) {
Core = ProcessorInfoBuffer.Location.Core;
}
//
// Collect CPU max thread count info.
//
if (Thread < ProcessorInfoBuffer.Location.Thread) {
Thread = ProcessorInfoBuffer.Location.Thread;
}
}
CpuStatus->PackageCount = Package + 1;
CpuStatus->MaxCoreCount = Core + 1;
CpuStatus->MaxThreadCount = Thread + 1;
DEBUG ((
DEBUG_INFO,
"Processor Info: Package: %d, MaxCore : %d, MaxThread: %d\n",
CpuStatus->PackageCount,
CpuStatus->MaxCoreCount,
CpuStatus->MaxThreadCount
));
//
// Collect valid core count in each package because not all cores are valid.
//
ThreadCountPerPackage = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (UINT32) * CpuStatus->PackageCount));
ASSERT (ThreadCountPerPackage != NULL);
ZeroMem (ThreadCountPerPackage, sizeof (UINT32) * CpuStatus->PackageCount);
CpuStatus->ThreadCountPerPackage = (EFI_PHYSICAL_ADDRESS)(UINTN)ThreadCountPerPackage;
ThreadCountPerCore = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (UINT8) * CpuStatus->PackageCount * CpuStatus->MaxCoreCount));
ASSERT (ThreadCountPerCore != NULL);
ZeroMem (ThreadCountPerCore, sizeof (UINT8) * CpuStatus->PackageCount * CpuStatus->MaxCoreCount);
CpuStatus->ThreadCountPerCore = (EFI_PHYSICAL_ADDRESS)(UINTN)ThreadCountPerCore;
for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) {
Location = &CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.ProcessorInfo.Location;
ThreadCountPerPackage[Location->Package]++;
ThreadCountPerCore[Location->Package * CpuStatus->MaxCoreCount + Location->Core]++;
}
for (PackageIndex = 0; PackageIndex < CpuStatus->PackageCount; PackageIndex++) {
if (ThreadCountPerPackage[PackageIndex] != 0) {
DEBUG ((DEBUG_INFO, "P%02d: Thread Count = %d\n", PackageIndex, ThreadCountPerPackage[PackageIndex]));
for (CoreIndex = 0; CoreIndex < CpuStatus->MaxCoreCount; CoreIndex++) {
if (ThreadCountPerCore[PackageIndex * CpuStatus->MaxCoreCount + CoreIndex] != 0) {
DEBUG ((
DEBUG_INFO,
" P%02d C%04d, Thread Count = %d\n",
PackageIndex,
CoreIndex,
ThreadCountPerCore[PackageIndex * CpuStatus->MaxCoreCount + CoreIndex]
));
}
}
}
}
CpuFeaturesData->CpuFlags.CoreSemaphoreCount = AllocateZeroPool (sizeof (UINT32) * CpuStatus->PackageCount * CpuStatus->MaxCoreCount * CpuStatus->MaxThreadCount);
ASSERT (CpuFeaturesData->CpuFlags.CoreSemaphoreCount != NULL);
CpuFeaturesData->CpuFlags.PackageSemaphoreCount = AllocateZeroPool (sizeof (UINT32) * CpuStatus->PackageCount * CpuStatus->MaxCoreCount * CpuStatus->MaxThreadCount);
ASSERT (CpuFeaturesData->CpuFlags.PackageSemaphoreCount != NULL);
//
// Initialize CpuFeaturesData->InitOrder[].CpuInfo.First
// Use AllocatePages () instead of AllocatePool () because pool cannot be freed in PEI phase but page can.
//
Pages = EFI_SIZE_TO_PAGES (CpuStatus->PackageCount * sizeof (UINT32) + CpuStatus->PackageCount * CpuStatus->MaxCoreCount * sizeof (UINT32));
FirstCore = AllocatePages (Pages);
ASSERT (FirstCore != NULL);
FirstThread = FirstCore + CpuStatus->PackageCount;
//
// Set FirstPackage, FirstCore[], FirstThread[] to maximum package ID, core ID, thread ID.
//
FirstPackage = MAX_UINT32;
SetMem32 (FirstCore, CpuStatus->PackageCount * sizeof (UINT32), MAX_UINT32);
SetMem32 (FirstThread, CpuStatus->PackageCount * CpuStatus->MaxCoreCount * sizeof (UINT32), MAX_UINT32);
for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) {
Location = &CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.ProcessorInfo.Location;
//
// Save the minimum package ID in the platform.
//
FirstPackage = MIN (Location->Package, FirstPackage);
//
// Save the minimum core ID per package.
//
FirstCore[Location->Package] = MIN (Location->Core, FirstCore[Location->Package]);
//
// Save the minimum thread ID per core.
//
FirstThread[Location->Package * CpuStatus->MaxCoreCount + Location->Core] = MIN (
Location->Thread,
FirstThread[Location->Package * CpuStatus->MaxCoreCount + Location->Core]
);
}
//
// Update the First field.
//
for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) {
Location = &CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.ProcessorInfo.Location;
if (Location->Package == FirstPackage) {
CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.First.Package = 1;
}
//
// Set First.Die/Tile/Module for each thread assuming:
// single Die under each package, single Tile under each Die, single Module under each Tile
//
CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.First.Die = 1;
CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.First.Tile = 1;
CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.First.Module = 1;
if (Location->Core == FirstCore[Location->Package]) {
CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.First.Core = 1;
}
if (Location->Thread == FirstThread[Location->Package * CpuStatus->MaxCoreCount + Location->Core]) {
CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.First.Thread = 1;
}
}
FreePages (FirstCore, Pages);
}
/**
Worker function to do OR operation on CPU feature supported bits mask buffer.
@param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer
@param[in] OrFeatureBitMask The feature bit mask to do OR operation
@param[in] BitMaskSize The CPU feature bits mask buffer size.
**/
VOID
SupportedMaskOr (
IN UINT8 *SupportedFeatureMask,
IN UINT8 *OrFeatureBitMask,
IN UINT32 BitMaskSize
)
{
UINTN Index;
UINT8 *Data1;
UINT8 *Data2;
Data1 = SupportedFeatureMask;
Data2 = OrFeatureBitMask;
for (Index = 0; Index < BitMaskSize; Index++) {
*(Data1++) |= *(Data2++);
}
}
/**
Worker function to do AND operation on CPU feature supported bits mask buffer.
@param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer
@param[in] AndFeatureBitMask The feature bit mask to do AND operation
@param[in] BitMaskSize CPU feature bits mask buffer size.
**/
VOID
SupportedMaskAnd (
IN UINT8 *SupportedFeatureMask,
IN CONST UINT8 *AndFeatureBitMask,
IN UINT32 BitMaskSize
)
{
UINTN Index;
UINT8 *Data1;
CONST UINT8 *Data2;
Data1 = SupportedFeatureMask;
Data2 = AndFeatureBitMask;
for (Index = 0; Index < BitMaskSize; Index++) {
*(Data1++) &= *(Data2++);
}
}
/**
Worker function to clean bit operation on CPU feature supported bits mask buffer.
@param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer
@param[in] AndFeatureBitMask The feature bit mask to do XOR operation
@param[in] BitMaskSize CPU feature bits mask buffer size.
**/
VOID
SupportedMaskCleanBit (
IN UINT8 *SupportedFeatureMask,
IN UINT8 *AndFeatureBitMask,
IN UINT32 BitMaskSize
)
{
UINTN Index;
UINT8 *Data1;
UINT8 *Data2;
Data1 = SupportedFeatureMask;
Data2 = AndFeatureBitMask;
for (Index = 0; Index < BitMaskSize; Index++) {
*(Data1++) &= ~(*(Data2++));
}
}
/**
Worker function to check if the compared CPU feature set in the CPU feature
supported bits mask buffer.
@param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer
@param[in] ComparedFeatureBitMask The feature bit mask to be compared
@param[in] BitMaskSize CPU feature bits mask buffer size.
@retval TRUE The ComparedFeatureBitMask is set in CPU feature supported bits
mask buffer.
@retval FALSE The ComparedFeatureBitMask is not set in CPU feature supported bits
mask buffer.
**/
BOOLEAN
IsBitMaskMatch (
IN UINT8 *SupportedFeatureMask,
IN UINT8 *ComparedFeatureBitMask,
IN UINT32 BitMaskSize
)
{
UINTN Index;
UINT8 *Data1;
UINT8 *Data2;
Data1 = SupportedFeatureMask;
Data2 = ComparedFeatureBitMask;
for (Index = 0; Index < BitMaskSize; Index++) {
if (((*(Data1++)) & (*(Data2++))) != 0) {
return TRUE;
}
}
return FALSE;
}
/**
Collects processor data for calling processor.
@param[in,out] Buffer The pointer to private data buffer.
**/
VOID
EFIAPI
CollectProcessorData (
IN OUT VOID *Buffer
)
{
UINTN ProcessorNumber;
CPU_FEATURES_ENTRY *CpuFeature;
REGISTER_CPU_FEATURE_INFORMATION *CpuInfo;
LIST_ENTRY *Entry;
CPU_FEATURES_DATA *CpuFeaturesData;
CpuFeaturesData = (CPU_FEATURES_DATA *)Buffer;
ProcessorNumber = GetProcessorIndex (CpuFeaturesData);
CpuInfo = &CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo;
//
// collect processor information
//
FillProcessorInfo (CpuInfo);
Entry = GetFirstNode (&CpuFeaturesData->FeatureList);
while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) {
CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (Entry);
if (CpuFeature->SupportFunc == NULL) {
//
// If SupportFunc is NULL, then the feature is supported.
//
SupportedMaskOr (
CpuFeaturesData->InitOrder[ProcessorNumber].FeaturesSupportedMask,
CpuFeature->FeatureMask,
CpuFeaturesData->BitMaskSize
);
} else if (CpuFeature->SupportFunc (ProcessorNumber, CpuInfo, CpuFeature->ConfigData)) {
SupportedMaskOr (
CpuFeaturesData->InitOrder[ProcessorNumber].FeaturesSupportedMask,
CpuFeature->FeatureMask,
CpuFeaturesData->BitMaskSize
);
}
Entry = Entry->ForwardLink;
}
}
/**
Dump the contents of a CPU register table.
@param[in] ProcessorNumber The index of the CPU to show the register table contents
@note This service could be called by BSP only.
**/
VOID
DumpRegisterTableOnProcessor (
IN UINTN ProcessorNumber
)
{
CPU_FEATURES_DATA *CpuFeaturesData;
UINTN FeatureIndex;
CPU_REGISTER_TABLE *RegisterTable;
CPU_REGISTER_TABLE_ENTRY *RegisterTableEntry;
CPU_REGISTER_TABLE_ENTRY *RegisterTableEntryHead;
UINT32 DebugPrintErrorLevel;
DebugPrintErrorLevel = (ProcessorNumber == 0) ? DEBUG_INFO : DEBUG_VERBOSE;
CpuFeaturesData = GetCpuFeaturesData ();
//
// Debug information
//
RegisterTable = &CpuFeaturesData->RegisterTable[ProcessorNumber];
DEBUG ((DebugPrintErrorLevel, "RegisterTable->TableLength = %d\n", RegisterTable->TableLength));
RegisterTableEntryHead = (CPU_REGISTER_TABLE_ENTRY *)(UINTN)RegisterTable->RegisterTableEntry;
for (FeatureIndex = 0; FeatureIndex < RegisterTable->TableLength; FeatureIndex++) {
RegisterTableEntry = &RegisterTableEntryHead[FeatureIndex];
switch (RegisterTableEntry->RegisterType) {
case Msr:
DEBUG ((
DebugPrintErrorLevel,
"Processor: %04d: Index %04d, MSR : %08x, Bit Start: %02d, Bit Length: %02d, Value: %016lx\r\n",
(UINT32)ProcessorNumber,
(UINT32)FeatureIndex,
RegisterTableEntry->Index,
RegisterTableEntry->ValidBitStart,
RegisterTableEntry->ValidBitLength,
RegisterTableEntry->Value
));
break;
case ControlRegister:
DEBUG ((
DebugPrintErrorLevel,
"Processor: %04d: Index %04d, CR : %08x, Bit Start: %02d, Bit Length: %02d, Value: %016lx\r\n",
(UINT32)ProcessorNumber,
(UINT32)FeatureIndex,
RegisterTableEntry->Index,
RegisterTableEntry->ValidBitStart,
RegisterTableEntry->ValidBitLength,
RegisterTableEntry->Value
));
break;
case MemoryMapped:
DEBUG ((
DebugPrintErrorLevel,
"Processor: %04d: Index %04d, MMIO : %016lx, Bit Start: %02d, Bit Length: %02d, Value: %016lx\r\n",
(UINT32)ProcessorNumber,
(UINT32)FeatureIndex,
RegisterTableEntry->Index | LShiftU64 (RegisterTableEntry->HighIndex, 32),
RegisterTableEntry->ValidBitStart,
RegisterTableEntry->ValidBitLength,
RegisterTableEntry->Value
));
break;
case CacheControl:
DEBUG ((
DebugPrintErrorLevel,
"Processor: %04d: Index %04d, CACHE: %08x, Bit Start: %02d, Bit Length: %02d, Value: %016lx\r\n",
(UINT32)ProcessorNumber,
(UINT32)FeatureIndex,
RegisterTableEntry->Index,
RegisterTableEntry->ValidBitStart,
RegisterTableEntry->ValidBitLength,
RegisterTableEntry->Value
));
break;
case Semaphore:
DEBUG ((
DebugPrintErrorLevel,
"Processor: %04d: Index %04d, SEMAP: %s\r\n",
(UINT32)ProcessorNumber,
(UINT32)FeatureIndex,
mDependTypeStr[MIN ((UINT32)RegisterTableEntry->Value, InvalidDepType)]
));
break;
default:
break;
}
}
}
/**
Get the biggest dependence type.
PackageDepType > CoreDepType > ThreadDepType > NoneDepType.
@param[in] BeforeDep Before dependence type.
@param[in] AfterDep After dependence type.
@param[in] NoneNeibBeforeDep Before dependence type for not neighborhood features.
@param[in] NoneNeibAfterDep After dependence type for not neighborhood features.
@retval Return the biggest dependence type.
**/
CPU_FEATURE_DEPENDENCE_TYPE
BiggestDep (
IN CPU_FEATURE_DEPENDENCE_TYPE BeforeDep,
IN CPU_FEATURE_DEPENDENCE_TYPE AfterDep,
IN CPU_FEATURE_DEPENDENCE_TYPE NoneNeibBeforeDep,
IN CPU_FEATURE_DEPENDENCE_TYPE NoneNeibAfterDep
)
{
CPU_FEATURE_DEPENDENCE_TYPE Bigger;
Bigger = MAX (BeforeDep, AfterDep);
Bigger = MAX (Bigger, NoneNeibBeforeDep);
return MAX (Bigger, NoneNeibAfterDep);
}
/**
Analysis register CPU features on each processor and save CPU setting in CPU register table.
@param[in] NumberOfCpus Number of processor in system
**/
VOID
AnalysisProcessorFeatures (
IN UINTN NumberOfCpus
)
{
EFI_STATUS Status;
UINTN ProcessorNumber;
CPU_FEATURES_ENTRY *CpuFeature;
CPU_FEATURES_ENTRY *CpuFeatureInOrder;
CPU_FEATURES_INIT_ORDER *CpuInitOrder;
REGISTER_CPU_FEATURE_INFORMATION *CpuInfo;
LIST_ENTRY *Entry;
CPU_FEATURES_DATA *CpuFeaturesData;
LIST_ENTRY *NextEntry;
CPU_FEATURES_ENTRY *NextCpuFeatureInOrder;
BOOLEAN Success;
CPU_FEATURE_DEPENDENCE_TYPE BeforeDep;
CPU_FEATURE_DEPENDENCE_TYPE AfterDep;
CPU_FEATURE_DEPENDENCE_TYPE NoneNeibBeforeDep;
CPU_FEATURE_DEPENDENCE_TYPE NoneNeibAfterDep;
CpuFeaturesData = GetCpuFeaturesData ();
CpuFeaturesData->CapabilityPcd = AllocatePool (CpuFeaturesData->BitMaskSize);
ASSERT (CpuFeaturesData->CapabilityPcd != NULL);
SetMem (CpuFeaturesData->CapabilityPcd, CpuFeaturesData->BitMaskSize, 0xFF);
for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) {
CpuInitOrder = &CpuFeaturesData->InitOrder[ProcessorNumber];
//
// Calculate the last capability on all processors
//
SupportedMaskAnd (CpuFeaturesData->CapabilityPcd, CpuInitOrder->FeaturesSupportedMask, CpuFeaturesData->BitMaskSize);
}
//
// Calculate the last setting
//
CpuFeaturesData->SettingPcd = AllocateCopyPool (CpuFeaturesData->BitMaskSize, CpuFeaturesData->CapabilityPcd);
ASSERT (CpuFeaturesData->SettingPcd != NULL);
SupportedMaskAnd (CpuFeaturesData->SettingPcd, PcdGetPtr (PcdCpuFeaturesSetting), CpuFeaturesData->BitMaskSize);
//
// Dump the last CPU feature list
//
DEBUG_CODE_BEGIN ();
DEBUG ((DEBUG_INFO, "Last CPU features list...\n"));
Entry = GetFirstNode (&CpuFeaturesData->FeatureList);
while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) {
CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (Entry);
if (IsBitMaskMatch (CpuFeature->FeatureMask, CpuFeaturesData->CapabilityPcd, CpuFeaturesData->BitMaskSize)) {
if (IsBitMaskMatch (CpuFeature->FeatureMask, CpuFeaturesData->SettingPcd, CpuFeaturesData->BitMaskSize)) {
DEBUG ((DEBUG_INFO, "[Enable ] "));
} else {
DEBUG ((DEBUG_INFO, "[Disable ] "));
}
} else {
DEBUG ((DEBUG_INFO, "[Unsupport] "));
}
DumpCpuFeature (CpuFeature, CpuFeaturesData->BitMaskSize);
Entry = Entry->ForwardLink;
}
DEBUG ((DEBUG_INFO, "PcdCpuFeaturesCapability:\n"));
DumpCpuFeatureMask (CpuFeaturesData->CapabilityPcd, CpuFeaturesData->BitMaskSize);
DEBUG ((DEBUG_INFO, "Origin PcdCpuFeaturesSetting:\n"));
DumpCpuFeatureMask (PcdGetPtr (PcdCpuFeaturesSetting), CpuFeaturesData->BitMaskSize);
DEBUG ((DEBUG_INFO, "Final PcdCpuFeaturesSetting:\n"));
DumpCpuFeatureMask (CpuFeaturesData->SettingPcd, CpuFeaturesData->BitMaskSize);
DEBUG_CODE_END ();
//
// Save PCDs and display CPU PCDs
//
SetCapabilityPcd (CpuFeaturesData->CapabilityPcd, CpuFeaturesData->BitMaskSize);
SetSettingPcd (CpuFeaturesData->SettingPcd, CpuFeaturesData->BitMaskSize);
for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) {
CpuInitOrder = &CpuFeaturesData->InitOrder[ProcessorNumber];
Entry = GetFirstNode (&CpuFeaturesData->FeatureList);
while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) {
//
// Insert each feature into processor's order list
//
CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (Entry);
if (IsBitMaskMatch (CpuFeature->FeatureMask, CpuFeaturesData->CapabilityPcd, CpuFeaturesData->BitMaskSize)) {
CpuFeatureInOrder = AllocateCopyPool (sizeof (CPU_FEATURES_ENTRY), CpuFeature);
ASSERT (CpuFeatureInOrder != NULL);
InsertTailList (&CpuInitOrder->OrderList, &CpuFeatureInOrder->Link);
}
Entry = Entry->ForwardLink;
}
//
// Go through ordered feature list to initialize CPU features
//
CpuInfo = &CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo;
Entry = GetFirstNode (&CpuInitOrder->OrderList);
while (!IsNull (&CpuInitOrder->OrderList, Entry)) {
CpuFeatureInOrder = CPU_FEATURE_ENTRY_FROM_LINK (Entry);
Success = FALSE;
if (IsBitMaskMatch (CpuFeatureInOrder->FeatureMask, CpuFeaturesData->SettingPcd, CpuFeaturesData->BitMaskSize)) {
Status = CpuFeatureInOrder->InitializeFunc (ProcessorNumber, CpuInfo, CpuFeatureInOrder->ConfigData, TRUE);
if (EFI_ERROR (Status)) {
//
// Clean the CpuFeatureInOrder->FeatureMask in setting PCD.
//
SupportedMaskCleanBit (CpuFeaturesData->SettingPcd, CpuFeatureInOrder->FeatureMask, CpuFeaturesData->BitMaskSize);
if (CpuFeatureInOrder->FeatureName != NULL) {
DEBUG ((DEBUG_WARN, "Warning :: Failed to enable Feature: Name = %a.\n", CpuFeatureInOrder->FeatureName));
} else {
DEBUG ((DEBUG_WARN, "Warning :: Failed to enable Feature: Mask = "));
DumpCpuFeatureMask (CpuFeatureInOrder->FeatureMask, CpuFeaturesData->BitMaskSize);
}
} else {
Success = TRUE;
}
} else {
Status = CpuFeatureInOrder->InitializeFunc (ProcessorNumber, CpuInfo, CpuFeatureInOrder->ConfigData, FALSE);
if (EFI_ERROR (Status)) {
if (CpuFeatureInOrder->FeatureName != NULL) {
DEBUG ((DEBUG_WARN, "Warning :: Failed to disable Feature: Name = %a.\n", CpuFeatureInOrder->FeatureName));
} else {
DEBUG ((DEBUG_WARN, "Warning :: Failed to disable Feature: Mask = "));
DumpCpuFeatureMask (CpuFeatureInOrder->FeatureMask, CpuFeaturesData->BitMaskSize);
}
} else {
Success = TRUE;
}
}
if (Success) {
NextEntry = Entry->ForwardLink;
if (!IsNull (&CpuInitOrder->OrderList, NextEntry)) {
NextCpuFeatureInOrder = CPU_FEATURE_ENTRY_FROM_LINK (NextEntry);
//
// If feature has dependence with the next feature (ONLY care core/package dependency).
// and feature initialize succeed, add sync semaphere here.
//
BeforeDep = DetectFeatureScope (CpuFeatureInOrder, TRUE, NextCpuFeatureInOrder->FeatureMask);
AfterDep = DetectFeatureScope (NextCpuFeatureInOrder, FALSE, CpuFeatureInOrder->FeatureMask);
//
// Check whether next feature has After type dependence with not neighborhood CPU
// Features in former CPU features.
//
NoneNeibAfterDep = DetectNoneNeighborhoodFeatureScope (NextCpuFeatureInOrder, FALSE, &CpuInitOrder->OrderList);
} else {
BeforeDep = NoneDepType;
AfterDep = NoneDepType;
NoneNeibAfterDep = NoneDepType;
}
//
// Check whether current feature has Before type dependence with none neighborhood
// CPU features in after Cpu features.
//
NoneNeibBeforeDep = DetectNoneNeighborhoodFeatureScope (CpuFeatureInOrder, TRUE, &CpuInitOrder->OrderList);
//
// Get the biggest dependence and add semaphore for it.
// PackageDepType > CoreDepType > ThreadDepType > NoneDepType.
//
BeforeDep = BiggestDep (BeforeDep, AfterDep, NoneNeibBeforeDep, NoneNeibAfterDep);
if (BeforeDep > ThreadDepType) {
CPU_REGISTER_TABLE_WRITE32 (ProcessorNumber, Semaphore, 0, BeforeDep);
}
}
Entry = Entry->ForwardLink;
}
//
// Dump PcdCpuFeaturesSetting again because this value maybe updated
// again during initialize the features.
//
DEBUG ((DEBUG_INFO, "Dump final value for PcdCpuFeaturesSetting:\n"));
DumpCpuFeatureMask (CpuFeaturesData->SettingPcd, CpuFeaturesData->BitMaskSize);
//
// Dump the RegisterTable
//
DumpRegisterTableOnProcessor (ProcessorNumber);
}
}
/**
Increment semaphore by 1.
@param Sem IN: 32-bit unsigned integer
**/
VOID
LibReleaseSemaphore (
IN OUT volatile UINT32 *Sem
)
{
InterlockedIncrement (Sem);
}
/**
Decrement the semaphore by 1 if it is not zero.
Performs an atomic decrement operation for semaphore.
The compare exchange operation must be performed using
MP safe mechanisms.
@param Sem IN: 32-bit unsigned integer
**/
VOID
LibWaitForSemaphore (
IN OUT volatile UINT32 *Sem
)
{
UINT32 Value;
do {
Value = *Sem;
} while (Value == 0 ||
InterlockedCompareExchange32 (
Sem,
Value,
Value - 1
) != Value);
}
/**
Read / write CR value.
@param[in] CrIndex The CR index which need to read/write.
@param[in] Read Read or write. TRUE is read.
@param[in,out] CrValue CR value.
@retval EFI_SUCCESS means read/write success, else return EFI_UNSUPPORTED.
**/
UINTN
ReadWriteCr (
IN UINT32 CrIndex,
IN BOOLEAN Read,
IN OUT UINTN *CrValue
)
{
switch (CrIndex) {
case 0:
if (Read) {
*CrValue = AsmReadCr0 ();
} else {
AsmWriteCr0 (*CrValue);
}
break;
case 2:
if (Read) {
*CrValue = AsmReadCr2 ();
} else {
AsmWriteCr2 (*CrValue);
}
break;
case 3:
if (Read) {
*CrValue = AsmReadCr3 ();
} else {
AsmWriteCr3 (*CrValue);
}
break;
case 4:
if (Read) {
*CrValue = AsmReadCr4 ();
} else {
AsmWriteCr4 (*CrValue);
}
break;
default:
return EFI_UNSUPPORTED;
}
return EFI_SUCCESS;
}
/**
Initialize the CPU registers from a register table.
@param[in] RegisterTable The register table for this AP.
@param[in] ApLocation AP location info for this ap.
@param[in] CpuStatus CPU status info for this CPU.
@param[in] CpuFlags Flags data structure used when program the register.
@note This service could be called by BSP/APs.
**/
VOID
ProgramProcessorRegister (
IN CPU_REGISTER_TABLE *RegisterTable,
IN EFI_CPU_PHYSICAL_LOCATION *ApLocation,
IN CPU_STATUS_INFORMATION *CpuStatus,
IN PROGRAM_CPU_REGISTER_FLAGS *CpuFlags
)
{
CPU_REGISTER_TABLE_ENTRY *RegisterTableEntry;
UINTN Index;
UINTN Value;
CPU_REGISTER_TABLE_ENTRY *RegisterTableEntryHead;
volatile UINT32 *SemaphorePtr;
UINT32 FirstThread;
UINT32 CurrentThread;
UINT32 CurrentCore;
UINTN ProcessorIndex;
UINT32 *ThreadCountPerPackage;
UINT8 *ThreadCountPerCore;
EFI_STATUS Status;
UINT64 CurrentValue;
//
// Traverse Register Table of this logical processor
//
RegisterTableEntryHead = (CPU_REGISTER_TABLE_ENTRY *)(UINTN)RegisterTable->RegisterTableEntry;
for (Index = 0; Index < RegisterTable->TableLength; Index++) {
RegisterTableEntry = &RegisterTableEntryHead[Index];
//
// Check the type of specified register
//
switch (RegisterTableEntry->RegisterType) {
//
// The specified register is Control Register
//
case ControlRegister:
Status = ReadWriteCr (RegisterTableEntry->Index, TRUE, &Value);
if (EFI_ERROR (Status)) {
break;
}
if (RegisterTableEntry->TestThenWrite) {
CurrentValue = BitFieldRead64 (
Value,
RegisterTableEntry->ValidBitStart,
RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1
);
if (CurrentValue == RegisterTableEntry->Value) {
break;
}
}
Value = (UINTN)BitFieldWrite64 (
Value,
RegisterTableEntry->ValidBitStart,
RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1,
RegisterTableEntry->Value
);
ReadWriteCr (RegisterTableEntry->Index, FALSE, &Value);
break;
//
// The specified register is Model Specific Register
//
case Msr:
if (RegisterTableEntry->TestThenWrite) {
Value = (UINTN)AsmReadMsr64 (RegisterTableEntry->Index);
if (RegisterTableEntry->ValidBitLength >= 64) {
if (Value == RegisterTableEntry->Value) {
break;
}
} else {
CurrentValue = BitFieldRead64 (
Value,
RegisterTableEntry->ValidBitStart,
RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1
);
if (CurrentValue == RegisterTableEntry->Value) {
break;
}
}
}
if (RegisterTableEntry->ValidBitLength >= 64) {
//
// If length is not less than 64 bits, then directly write without reading
//
AsmWriteMsr64 (
RegisterTableEntry->Index,
RegisterTableEntry->Value
);
} else {
//
// Set the bit section according to bit start and length
//
AsmMsrBitFieldWrite64 (
RegisterTableEntry->Index,
RegisterTableEntry->ValidBitStart,
RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1,
RegisterTableEntry->Value
);
}
break;
//
// MemoryMapped operations
//
case MemoryMapped:
AcquireSpinLock (&CpuFlags->MemoryMappedLock);
MmioBitFieldWrite32 (
(UINTN)(RegisterTableEntry->Index | LShiftU64 (RegisterTableEntry->HighIndex, 32)),
RegisterTableEntry->ValidBitStart,
RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1,
(UINT32)RegisterTableEntry->Value
);
ReleaseSpinLock (&CpuFlags->MemoryMappedLock);
break;
//
// Enable or disable cache
//
case CacheControl:
//
// If value of the entry is 0, then disable cache. Otherwise, enable cache.
//
if (RegisterTableEntry->Value == 0) {
AsmDisableCache ();
} else {
AsmEnableCache ();
}
break;
case Semaphore:
// Semaphore works logic like below:
//
// V(x) = LibReleaseSemaphore (Semaphore[FirstThread + x]);
// P(x) = LibWaitForSemaphore (Semaphore[FirstThread + x]);
//
// All threads (T0...Tn) waits in P() line and continues running
// together.
//
//
// T0 T1 ... Tn
//
// V(0...n) V(0...n) ... V(0...n)
// n * P(0) n * P(1) ... n * P(n)
//
switch (RegisterTableEntry->Value) {
case CoreDepType:
SemaphorePtr = CpuFlags->CoreSemaphoreCount;
ThreadCountPerCore = (UINT8 *)(UINTN)CpuStatus->ThreadCountPerCore;
CurrentCore = ApLocation->Package * CpuStatus->MaxCoreCount + ApLocation->Core;
//
// Get Offset info for the first thread in the core which current thread belongs to.
//
FirstThread = CurrentCore * CpuStatus->MaxThreadCount;
CurrentThread = FirstThread + ApLocation->Thread;
//
// Different cores may have different valid threads in them. If driver maintail clearly
// thread index in different cores, the logic will be much complicated.
// Here driver just simply records the max thread number in all cores and use it as expect
// thread number for all cores.
// In below two steps logic, first current thread will Release semaphore for each thread
// in current core. Maybe some threads are not valid in this core, but driver don't
// care. Second, driver will let current thread wait semaphore for all valid threads in
// current core. Because only the valid threads will do release semaphore for this
// thread, driver here only need to wait the valid thread count.
//
//
// First Notify ALL THREADs in current Core that this thread is ready.
//
for (ProcessorIndex = 0; ProcessorIndex < CpuStatus->MaxThreadCount; ProcessorIndex++) {
LibReleaseSemaphore (&SemaphorePtr[FirstThread + ProcessorIndex]);
}
//
// Second, check whether all VALID THREADs (not all threads) in current core are ready.
//
for (ProcessorIndex = 0; ProcessorIndex < ThreadCountPerCore[CurrentCore]; ProcessorIndex++) {
LibWaitForSemaphore (&SemaphorePtr[CurrentThread]);
}
break;
case PackageDepType:
SemaphorePtr = CpuFlags->PackageSemaphoreCount;
ThreadCountPerPackage = (UINT32 *)(UINTN)CpuStatus->ThreadCountPerPackage;
//
// Get Offset info for the first thread in the package which current thread belongs to.
//
FirstThread = ApLocation->Package * CpuStatus->MaxCoreCount * CpuStatus->MaxThreadCount;
//
// Get the possible threads count for current package.
//
CurrentThread = FirstThread + CpuStatus->MaxThreadCount * ApLocation->Core + ApLocation->Thread;
//
// Different packages may have different valid threads in them. If driver maintail clearly
// thread index in different packages, the logic will be much complicated.
// Here driver just simply records the max thread number in all packages and use it as expect
// thread number for all packages.
// In below two steps logic, first current thread will Release semaphore for each thread
// in current package. Maybe some threads are not valid in this package, but driver don't
// care. Second, driver will let current thread wait semaphore for all valid threads in
// current package. Because only the valid threads will do release semaphore for this
// thread, driver here only need to wait the valid thread count.
//
//
// First Notify ALL THREADS in current package that this thread is ready.
//
for (ProcessorIndex = 0; ProcessorIndex < CpuStatus->MaxThreadCount * CpuStatus->MaxCoreCount; ProcessorIndex++) {
LibReleaseSemaphore (&SemaphorePtr[FirstThread + ProcessorIndex]);
}
//
// Second, check whether VALID THREADS (not all threads) in current package are ready.
//
for (ProcessorIndex = 0; ProcessorIndex < ThreadCountPerPackage[ApLocation->Package]; ProcessorIndex++) {
LibWaitForSemaphore (&SemaphorePtr[CurrentThread]);
}
break;
default:
break;
}
break;
default:
break;
}
}
}
/**
Programs registers for the calling processor.
@param[in,out] Buffer The pointer to private data buffer.
**/
VOID
EFIAPI
SetProcessorRegister (
IN OUT VOID *Buffer
)
{
CPU_FEATURES_DATA *CpuFeaturesData;
CPU_REGISTER_TABLE *RegisterTable;
CPU_REGISTER_TABLE *RegisterTables;
UINT32 InitApicId;
UINTN ProcIndex;
UINTN Index;
ACPI_CPU_DATA *AcpiCpuData;
CpuFeaturesData = (CPU_FEATURES_DATA *)Buffer;
AcpiCpuData = CpuFeaturesData->AcpiCpuData;
RegisterTables = (CPU_REGISTER_TABLE *)(UINTN)AcpiCpuData->CpuFeatureInitData.RegisterTable;
InitApicId = GetInitialApicId ();
RegisterTable = NULL;
ProcIndex = (UINTN)-1;
for (Index = 0; Index < AcpiCpuData->NumberOfCpus; Index++) {
if (RegisterTables[Index].InitialApicId == InitApicId) {
RegisterTable = &RegisterTables[Index];
ProcIndex = Index;
break;
}
}
ASSERT (RegisterTable != NULL);
ProgramProcessorRegister (
RegisterTable,
(EFI_CPU_PHYSICAL_LOCATION *)(UINTN)AcpiCpuData->CpuFeatureInitData.ApLocation + ProcIndex,
&AcpiCpuData->CpuFeatureInitData.CpuStatus,
&CpuFeaturesData->CpuFlags
);
}
/**
Performs CPU features detection.
This service will invoke MP service to check CPU features'
capabilities on BSP/APs.
@note This service could be called by BSP only.
**/
VOID
EFIAPI
CpuFeaturesDetect (
VOID
)
{
CPU_FEATURES_DATA *CpuFeaturesData;
CpuFeaturesData = GetCpuFeaturesData ();
CpuInitDataInitialize ();
if (CpuFeaturesData->NumberOfCpus > 1) {
//
// Wakeup all APs for data collection.
//
StartupAllAPsWorker (CollectProcessorData, NULL);
}
//
// Collect data on BSP
//
CollectProcessorData (CpuFeaturesData);
AnalysisProcessorFeatures (CpuFeaturesData->NumberOfCpus);
}