/** @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); | |
} |