X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=UefiCpuPkg%2FLibrary%2FMpInitLib%2FMpLib.c;h=d0fbc17ce5516ae0ab07bde8460fa07f185b8426;hb=348a34d984d5265ae91a6a56f0ccbc613210238d;hp=18060fd0998c0323b37c1a0a8a30bd437ca626a0;hpb=861218740d6d0f6b443f4ef6e170f9524372add8;p=mirror_edk2.git
diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c
index 18060fd099..d0fbc17ce5 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
@@ -1,14 +1,8 @@
/** @file
CPU MP Initialize Library common functions.
- Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.
- This program and the accompanying materials
- are licensed and made available under the terms and conditions of the BSD License
- which accompanies this distribution. The full text of the license may be found at
- http://opensource.org/licenses/bsd-license.php
-
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
- WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+ Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
**/
@@ -195,6 +189,10 @@ SaveVolatileRegisters (
VolatileRegisters->Dr6 = AsmReadDr6 ();
VolatileRegisters->Dr7 = AsmReadDr7 ();
}
+
+ AsmReadGdtr (&VolatileRegisters->Gdtr);
+ AsmReadIdtr (&VolatileRegisters->Idtr);
+ VolatileRegisters->Tr = AsmReadTr ();
}
/**
@@ -211,10 +209,11 @@ RestoreVolatileRegisters (
)
{
CPUID_VERSION_INFO_EDX VersionInfoEdx;
+ IA32_TSS_DESCRIPTOR *Tss;
- AsmWriteCr0 (VolatileRegisters->Cr0);
AsmWriteCr3 (VolatileRegisters->Cr3);
AsmWriteCr4 (VolatileRegisters->Cr4);
+ AsmWriteCr0 (VolatileRegisters->Cr0);
if (IsRestoreDr) {
AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32);
@@ -231,6 +230,18 @@ RestoreVolatileRegisters (
AsmWriteDr7 (VolatileRegisters->Dr7);
}
}
+
+ AsmWriteGdtr (&VolatileRegisters->Gdtr);
+ AsmWriteIdtr (&VolatileRegisters->Idtr);
+ if (VolatileRegisters->Tr != 0 &&
+ VolatileRegisters->Tr < VolatileRegisters->Gdtr.Limit) {
+ Tss = (IA32_TSS_DESCRIPTOR *)(VolatileRegisters->Gdtr.Base +
+ VolatileRegisters->Tr);
+ if (Tss->Bits.P == 1) {
+ Tss->Bits.Type &= 0xD; // 1101 - Clear busy bit just in case
+ AsmWriteTr (VolatileRegisters->Tr);
+ }
+ }
}
/**
@@ -313,6 +324,7 @@ SortApicId (
CPU_INFO_IN_HOB CpuInfo;
UINT32 ApCount;
CPU_INFO_IN_HOB *CpuInfoInHob;
+ volatile UINT32 *StartupApSignal;
ApCount = CpuMpData->CpuCount - 1;
CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
@@ -337,6 +349,14 @@ SortApicId (
sizeof (CPU_INFO_IN_HOB)
);
CopyMem (&CpuInfoInHob[Index1], &CpuInfo, sizeof (CPU_INFO_IN_HOB));
+
+ //
+ // Also exchange the StartupApSignal.
+ //
+ StartupApSignal = CpuMpData->CpuData[Index3].StartupApSignal;
+ CpuMpData->CpuData[Index3].StartupApSignal =
+ CpuMpData->CpuData[Index1].StartupApSignal;
+ CpuMpData->CpuData[Index1].StartupApSignal = StartupApSignal;
}
}
@@ -379,12 +399,16 @@ ApInitializeSync (
)
{
CPU_MP_DATA *CpuMpData;
+ UINTN ProcessorNumber;
+ EFI_STATUS Status;
CpuMpData = (CPU_MP_DATA *) Buffer;
+ Status = GetProcessorNumber (CpuMpData, &ProcessorNumber);
+ ASSERT_EFI_ERROR (Status);
//
// Load microcode on AP
//
- MicrocodeDetect (CpuMpData);
+ MicrocodeDetect (CpuMpData, ProcessorNumber);
//
// Sync BSP's MTRR table to AP
//
@@ -409,16 +433,19 @@ GetProcessorNumber (
UINTN TotalProcessorNumber;
UINTN Index;
CPU_INFO_IN_HOB *CpuInfoInHob;
+ UINT32 CurrentApicId;
CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
TotalProcessorNumber = CpuMpData->CpuCount;
+ CurrentApicId = GetApicId ();
for (Index = 0; Index < TotalProcessorNumber; Index ++) {
- if (CpuInfoInHob[Index].ApicId == GetApicId ()) {
+ if (CpuInfoInHob[Index].ApicId == CurrentApicId) {
*ProcessorNumber = Index;
return EFI_SUCCESS;
}
}
+
return EFI_NOT_FOUND;
}
@@ -435,13 +462,14 @@ CollectProcessorCount (
)
{
UINTN Index;
+ CPU_INFO_IN_HOB *CpuInfoInHob;
+ BOOLEAN X2Apic;
//
// Send 1st broadcast IPI to APs to wakeup APs
//
- CpuMpData->InitFlag = ApInitConfig;
- CpuMpData->X2ApicEnable = FALSE;
- WakeUpAP (CpuMpData, TRUE, 0, NULL, NULL);
+ CpuMpData->InitFlag = ApInitConfig;
+ WakeUpAP (CpuMpData, TRUE, 0, NULL, NULL, TRUE);
CpuMpData->InitFlag = ApInitDone;
ASSERT (CpuMpData->CpuCount <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
//
@@ -451,18 +479,34 @@ CollectProcessorCount (
CpuPause ();
}
+
+ //
+ // Enable x2APIC mode if
+ // 1. Number of CPU is greater than 255; or
+ // 2. There are any logical processors reporting an Initial APIC ID of 255 or greater.
+ //
+ X2Apic = FALSE;
if (CpuMpData->CpuCount > 255) {
//
// If there are more than 255 processor found, force to enable X2APIC
//
- CpuMpData->X2ApicEnable = TRUE;
+ X2Apic = TRUE;
+ } else {
+ CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
+ for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
+ if (CpuInfoInHob[Index].InitialApicId >= 0xFF) {
+ X2Apic = TRUE;
+ break;
+ }
+ }
}
- if (CpuMpData->X2ApicEnable) {
+
+ if (X2Apic) {
DEBUG ((DEBUG_INFO, "Force x2APIC mode!\n"));
//
// Wakeup all APs to enable x2APIC mode
//
- WakeUpAP (CpuMpData, TRUE, 0, ApFuncEnableX2Apic, NULL);
+ WakeUpAP (CpuMpData, TRUE, 0, ApFuncEnableX2Apic, NULL, TRUE);
//
// Wait for all known APs finished
//
@@ -508,7 +552,8 @@ InitializeApData (
IN UINT64 ApTopOfStack
)
{
- CPU_INFO_IN_HOB *CpuInfoInHob;
+ CPU_INFO_IN_HOB *CpuInfoInHob;
+ MSR_IA32_PLATFORM_ID_REGISTER PlatformIdMsr;
CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId ();
@@ -518,15 +563,17 @@ InitializeApData (
CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE;
CpuMpData->CpuData[ProcessorNumber].CpuHealthy = (BistData == 0) ? TRUE : FALSE;
- if (CpuInfoInHob[ProcessorNumber].InitialApicId >= 0xFF) {
- //
- // Set x2APIC mode if there are any logical processor reporting
- // an Initial APIC ID of 255 or greater.
- //
- AcquireSpinLock(&CpuMpData->MpLock);
- CpuMpData->X2ApicEnable = TRUE;
- ReleaseSpinLock(&CpuMpData->MpLock);
- }
+
+ PlatformIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID);
+ CpuMpData->CpuData[ProcessorNumber].PlatformId = (UINT8) PlatformIdMsr.Bits.PlatformId;
+
+ AsmCpuid (
+ CPUID_VERSION_INFO,
+ &CpuMpData->CpuData[ProcessorNumber].ProcessorSignature,
+ NULL,
+ NULL,
+ NULL
+ );
InitializeSpinLock(&CpuMpData->CpuData[ProcessorNumber].ApLock);
SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);
@@ -536,7 +583,7 @@ InitializeApData (
This function will be called from AP reset code if BSP uses WakeUpAP.
@param[in] ExchangeInfo Pointer to the MP exchange info buffer
- @param[in] NumApsExecuting Number of current executing AP
+ @param[in] ApIndex Number of current executing AP
**/
VOID
EFIAPI
@@ -565,6 +612,10 @@ ApWakeupFunction (
// We need to re-initialize them at here
//
ProgramVirtualWireMode ();
+ //
+ // Mask the LINT0 and LINT1 so that AP doesn't enter the system timer interrupt handler.
+ //
+ DisableLvtInterrupts ();
SyncLocalApicTimerSetting (CpuMpData);
CurrentApicMode = GetApicMode ();
@@ -581,15 +632,15 @@ ApWakeupFunction (
ApTopOfStack = CpuMpData->Buffer + (ProcessorNumber + 1) * CpuMpData->CpuApStackSize;
BistData = *(UINT32 *) ((UINTN) ApTopOfStack - sizeof (UINTN));
//
- // Do some AP initialize sync
- //
- ApInitializeSync (CpuMpData);
- //
- // Sync BSP's Control registers to APs
+ // CpuMpData->CpuData[0].VolatileRegisters is initialized based on BSP environment,
+ // to initialize AP in InitConfig path.
+ // NOTE: IDTR.BASE stored in CpuMpData->CpuData[0].VolatileRegisters points to a different IDT shared by all APs.
//
RestoreVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters, FALSE);
InitializeApData (CpuMpData, ProcessorNumber, BistData, ApTopOfStack);
ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal;
+
+ InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);
} else {
//
// Execute AP function if AP is ready
@@ -609,6 +660,13 @@ ApWakeupFunction (
// Restore AP's volatile registers saved
//
RestoreVolatileRegisters (&CpuMpData->CpuData[ProcessorNumber].VolatileRegisters, TRUE);
+ } else {
+ //
+ // The CPU driver might not flush TLB for APs on spot after updating
+ // page attributes. AP in mwait loop mode needs to take care of it when
+ // woken up.
+ //
+ CpuFlushTlb ();
}
if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateReady) {
@@ -618,7 +676,7 @@ ApWakeupFunction (
SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateBusy);
//
// Enable source debugging on AP function
- //
+ //
EnableDebugAgent ();
//
// Invoke AP function here
@@ -662,7 +720,6 @@ ApWakeupFunction (
// AP finished executing C code
//
InterlockedIncrement ((UINT32 *) &CpuMpData->FinishedCount);
- InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);
//
// Place AP is specified loop mode
@@ -751,6 +808,9 @@ FillExchangeInfoData (
)
{
volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;
+ UINTN Size;
+ IA32_SEGMENT_DESCRIPTOR *Selector;
+ IA32_CR4 Cr4;
ExchangeInfo = CpuMpData->MpCpuExchangeInfo;
ExchangeInfo->Lock = 0;
@@ -775,11 +835,63 @@ FillExchangeInfoData (
ExchangeInfo->InitializeFloatingPointUnitsAddress = (UINTN)InitializeFloatingPointUnits;
+ //
+ // We can check either CPUID(7).ECX[bit16] or check CR4.LA57[bit12]
+ // to determin whether 5-Level Paging is enabled.
+ // CPUID(7).ECX[bit16] shows CPU's capability, CR4.LA57[bit12] shows
+ // current system setting.
+ // Using latter way is simpler because it also eliminates the needs to
+ // check whether platform wants to enable it.
+ //
+ Cr4.UintN = AsmReadCr4 ();
+ ExchangeInfo->Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);
+ DEBUG ((DEBUG_INFO, "%a: 5-Level Paging = %d\n", gEfiCallerBaseName, ExchangeInfo->Enable5LevelPaging));
+
//
// Get the BSP's data of GDT and IDT
//
AsmReadGdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->GdtrProfile);
AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile);
+
+ //
+ // Find a 32-bit code segment
+ //
+ Selector = (IA32_SEGMENT_DESCRIPTOR *)ExchangeInfo->GdtrProfile.Base;
+ Size = ExchangeInfo->GdtrProfile.Limit + 1;
+ while (Size > 0) {
+ if (Selector->Bits.L == 0 && Selector->Bits.Type >= 8) {
+ ExchangeInfo->ModeTransitionSegment =
+ (UINT16)((UINTN)Selector - ExchangeInfo->GdtrProfile.Base);
+ break;
+ }
+ Selector += 1;
+ Size -= sizeof (IA32_SEGMENT_DESCRIPTOR);
+ }
+
+ //
+ // Copy all 32-bit code and 64-bit code into memory with type of
+ // EfiBootServicesCode to avoid page fault if NX memory protection is enabled.
+ //
+ if (CpuMpData->WakeupBufferHigh != 0) {
+ Size = CpuMpData->AddressMap.RendezvousFunnelSize -
+ CpuMpData->AddressMap.ModeTransitionOffset;
+ CopyMem (
+ (VOID *)CpuMpData->WakeupBufferHigh,
+ CpuMpData->AddressMap.RendezvousFunnelAddress +
+ CpuMpData->AddressMap.ModeTransitionOffset,
+ Size
+ );
+
+ ExchangeInfo->ModeTransitionMemory = (UINT32)CpuMpData->WakeupBufferHigh;
+ } else {
+ ExchangeInfo->ModeTransitionMemory = (UINT32)
+ (ExchangeInfo->BufferStart + CpuMpData->AddressMap.ModeTransitionOffset);
+ }
+
+ ExchangeInfo->ModeHighMemory = ExchangeInfo->ModeTransitionMemory +
+ (UINT32)ExchangeInfo->ModeOffset -
+ (UINT32)CpuMpData->AddressMap.ModeTransitionOffset;
+ ExchangeInfo->ModeHighSegment = (UINT16)ExchangeInfo->CodeSegment;
}
/**
@@ -855,6 +967,10 @@ AllocateResetVector (
CpuMpData->WakeupBuffer = GetWakeupBuffer (ApResetVectorSize);
CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)
(CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize);
+ CpuMpData->WakeupBufferHigh = GetModeTransitionBuffer (
+ CpuMpData->AddressMap.RendezvousFunnelSize -
+ CpuMpData->AddressMap.ModeTransitionOffset
+ );
}
BackupAndPrepareWakeupBuffer (CpuMpData);
}
@@ -881,6 +997,7 @@ FreeResetVector (
@param[in] ProcessorNumber The handle number of specified processor
@param[in] Procedure The function to be invoked by AP
@param[in] ProcedureArgument The argument to be passed into AP function
+ @param[in] WakeUpDisabledAps Whether need to wake up disabled APs in broadcast mode.
**/
VOID
WakeUpAP (
@@ -888,7 +1005,8 @@ WakeUpAP (
IN BOOLEAN Broadcast,
IN UINTN ProcessorNumber,
IN EFI_AP_PROCEDURE Procedure, OPTIONAL
- IN VOID *ProcedureArgument OPTIONAL
+ IN VOID *ProcedureArgument, OPTIONAL
+ IN BOOLEAN WakeUpDisabledAps
)
{
volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;
@@ -900,13 +1018,15 @@ WakeUpAP (
CpuMpData->FinishedCount = 0;
ResetVectorRequired = FALSE;
- if (CpuMpData->ApLoopMode == ApInHltLoop ||
+ if (CpuMpData->WakeUpByInitSipiSipi ||
CpuMpData->InitFlag != ApInitDone) {
ResetVectorRequired = TRUE;
AllocateResetVector (CpuMpData);
FillExchangeInfoData (CpuMpData);
SaveLocalApicTimerSetting (CpuMpData);
- } else if (CpuMpData->ApLoopMode == ApInMwaitLoop) {
+ }
+
+ if (CpuMpData->ApLoopMode == ApInMwaitLoop) {
//
// Get AP target C-state each time when waking up AP,
// for it maybe updated by platform again
@@ -920,6 +1040,15 @@ WakeUpAP (
for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
if (Index != CpuMpData->BspNumber) {
CpuData = &CpuMpData->CpuData[Index];
+ //
+ // All AP(include disabled AP) will be woke up by INIT-SIPI-SIPI, but
+ // the AP procedure will be skipped for disabled AP because AP state
+ // is not CpuStateReady.
+ //
+ if (GetApState (CpuData) == CpuStateDisabled && !WakeUpDisabledAps) {
+ continue;
+ }
+
CpuData->ApFunction = (UINTN) Procedure;
CpuData->ApFunctionArgument = (UINTN) ProcedureArgument;
SetApState (CpuData, CpuStateReady);
@@ -935,24 +1064,67 @@ WakeUpAP (
SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart);
}
if (CpuMpData->InitFlag == ApInitConfig) {
- //
- // Here support two methods to collect AP count through adjust
- // PcdCpuApInitTimeOutInMicroSeconds values.
- //
- // one way is set a value to just let the first AP to start the
- // initialization, then through the later while loop to wait all Aps
- // finsh the initialization.
- // The other way is set a value to let all APs finished the initialzation.
- // In this case, the later while loop is useless.
- //
- TimedWaitForApFinish (
- CpuMpData,
- PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1,
- PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds)
- );
+ if (PcdGet32 (PcdCpuBootLogicalProcessorNumber) > 0) {
+ //
+ // The AP enumeration algorithm below is suitable only when the
+ // platform can tell us the *exact* boot CPU count in advance.
+ //
+ // The wait below finishes only when the detected AP count reaches
+ // (PcdCpuBootLogicalProcessorNumber - 1), regardless of how long that
+ // takes. If at least one AP fails to check in (meaning a platform
+ // hardware bug), the detection hangs forever, by design. If the actual
+ // boot CPU count in the system is higher than
+ // PcdCpuBootLogicalProcessorNumber (meaning a platform
+ // misconfiguration), then some APs may complete initialization after
+ // the wait finishes, and cause undefined behavior.
+ //
+ TimedWaitForApFinish (
+ CpuMpData,
+ PcdGet32 (PcdCpuBootLogicalProcessorNumber) - 1,
+ MAX_UINT32 // approx. 71 minutes
+ );
+ } else {
+ //
+ // The AP enumeration algorithm below is suitable for two use cases.
+ //
+ // (1) The check-in time for an individual AP is bounded, and APs run
+ // through their initialization routines strongly concurrently. In
+ // particular, the number of concurrently running APs
+ // ("NumApsExecuting") is never expected to fall to zero
+ // *temporarily* -- it is expected to fall to zero only when all
+ // APs have checked-in.
+ //
+ // In this case, the platform is supposed to set
+ // PcdCpuApInitTimeOutInMicroSeconds to a low-ish value (just long
+ // enough for one AP to start initialization). The timeout will be
+ // reached soon, and remaining APs are collected by watching
+ // NumApsExecuting fall to zero. If NumApsExecuting falls to zero
+ // mid-process, while some APs have not completed initialization,
+ // the behavior is undefined.
+ //
+ // (2) The check-in time for an individual AP is unbounded, and/or APs
+ // may complete their initializations widely spread out. In
+ // particular, some APs may finish initialization before some APs
+ // even start.
+ //
+ // In this case, the platform is supposed to set
+ // PcdCpuApInitTimeOutInMicroSeconds to a high-ish value. The AP
+ // enumeration will always take that long (except when the boot CPU
+ // count happens to be maximal, that is,
+ // PcdCpuMaxLogicalProcessorNumber). All APs are expected to
+ // check-in before the timeout, and NumApsExecuting is assumed zero
+ // at timeout. APs that miss the time-out may cause undefined
+ // behavior.
+ //
+ TimedWaitForApFinish (
+ CpuMpData,
+ PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1,
+ PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds)
+ );
- while (CpuMpData->MpCpuExchangeInfo->NumApsExecuting != 0) {
- CpuPause();
+ while (CpuMpData->MpCpuExchangeInfo->NumApsExecuting != 0) {
+ CpuPause();
+ }
}
} else {
//
@@ -991,6 +1163,13 @@ WakeUpAP (
if (ResetVectorRequired) {
FreeResetVector (CpuMpData);
}
+
+ //
+ // After one round of Wakeup Ap actions, need to re-sync ApLoopMode with
+ // WakeUpByInitSipiSipi flag. WakeUpByInitSipiSipi flag maybe changed by
+ // S3SmmInitDone Ppi.
+ //
+ CpuMpData->WakeUpByInitSipiSipi = (CpuMpData->ApLoopMode == ApInHltLoop);
}
/**
@@ -1032,7 +1211,7 @@ CalculateTimeout (
//
// GetPerformanceCounterProperties () returns the timestamp counter's frequency
- // in Hz.
+ // in Hz.
//
TimestampCounterFreq = GetPerformanceCounterProperties (NULL, NULL);
@@ -1192,7 +1371,7 @@ ResetProcessorToIdleState (
CpuMpData = GetCpuMpData ();
CpuMpData->InitFlag = ApInitReconfig;
- WakeUpAP (CpuMpData, FALSE, ProcessorNumber, NULL, NULL);
+ WakeUpAP (CpuMpData, FALSE, ProcessorNumber, NULL, NULL, TRUE);
while (CpuMpData->FinishedCount < 1) {
CpuPause ();
}
@@ -1255,9 +1434,9 @@ CheckThisAP (
CpuData = &CpuMpData->CpuData[ProcessorNumber];
//
- // Check the CPU state of AP. If it is CpuStateFinished, then the AP has finished its task.
+ // Check the CPU state of AP. If it is CpuStateIdle, then the AP has finished its task.
// Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the
- // value of state after setting the it to CpuStateFinished, so BSP can safely make use of its value.
+ // value of state after setting the it to CpuStateIdle, so BSP can safely make use of its value.
//
//
// If the AP finishes for StartupThisAP(), return EFI_SUCCESS.
@@ -1323,12 +1502,12 @@ CheckAllAPs (
CpuData = &CpuMpData->CpuData[ProcessorNumber];
//
- // Check the CPU state of AP. If it is CpuStateFinished, then the AP has finished its task.
+ // Check the CPU state of AP. If it is CpuStateIdle, then the AP has finished its task.
// Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the
- // value of state after setting the it to CpuStateFinished, so BSP can safely make use of its value.
+ // value of state after setting the it to CpuStateIdle, so BSP can safely make use of its value.
//
if (GetApState(CpuData) == CpuStateFinished) {
- CpuMpData->RunningCount ++;
+ CpuMpData->RunningCount --;
CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE;
SetApState(CpuData, CpuStateIdle);
@@ -1344,7 +1523,8 @@ CheckAllAPs (
FALSE,
(UINT32) NextProcessorNumber,
CpuMpData->Procedure,
- CpuMpData->ProcArguments
+ CpuMpData->ProcArguments,
+ TRUE
);
}
}
@@ -1354,7 +1534,7 @@ CheckAllAPs (
//
// If all APs finish, return EFI_SUCCESS.
//
- if (CpuMpData->RunningCount == CpuMpData->StartCount) {
+ if (CpuMpData->RunningCount == 0) {
return EFI_SUCCESS;
}
@@ -1371,7 +1551,7 @@ CheckAllAPs (
//
if (CpuMpData->FailedCpuList != NULL) {
*CpuMpData->FailedCpuList =
- AllocatePool ((CpuMpData->StartCount - CpuMpData->FinishedCount + 1) * sizeof (UINTN));
+ AllocatePool ((CpuMpData->RunningCount + 1) * sizeof (UINTN));
ASSERT (*CpuMpData->FailedCpuList != NULL);
}
ListIndex = 0;
@@ -1423,6 +1603,7 @@ MpInitLibInitialize (
UINT32 MaxLogicalProcessorNumber;
UINT32 ApStackSize;
MP_ASSEMBLY_ADDRESS_MAP AddressMap;
+ CPU_VOLATILE_REGISTERS VolatileRegisters;
UINTN BufferSize;
UINT32 MonitorFilterSize;
VOID *MpBuffer;
@@ -1433,6 +1614,7 @@ MpInitLibInitialize (
UINTN Index;
UINTN ApResetVectorSize;
UINTN BackupBufferAddr;
+ UINTN ApIdtBase;
OldCpuMpData = GetCpuMpDataFromGuidedHob ();
if (OldCpuMpData == NULL) {
@@ -1447,19 +1629,48 @@ MpInitLibInitialize (
ApStackSize = PcdGet32(PcdCpuApStackSize);
ApLoopMode = GetApLoopMode (&MonitorFilterSize);
+ //
+ // Save BSP's Control registers for APs.
+ //
+ SaveVolatileRegisters (&VolatileRegisters);
+
BufferSize = ApStackSize * MaxLogicalProcessorNumber;
BufferSize += MonitorFilterSize * MaxLogicalProcessorNumber;
- BufferSize += sizeof (CPU_MP_DATA);
BufferSize += ApResetVectorSize;
+ BufferSize = ALIGN_VALUE (BufferSize, 8);
+ BufferSize += VolatileRegisters.Idtr.Limit + 1;
+ BufferSize += sizeof (CPU_MP_DATA);
BufferSize += (sizeof (CPU_AP_DATA) + sizeof (CPU_INFO_IN_HOB))* MaxLogicalProcessorNumber;
MpBuffer = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize));
ASSERT (MpBuffer != NULL);
ZeroMem (MpBuffer, BufferSize);
Buffer = (UINTN) MpBuffer;
+ //
+ // The layout of the Buffer is as below:
+ //
+ // +--------------------+ <-- Buffer
+ // AP Stacks (N)
+ // +--------------------+ <-- MonitorBuffer
+ // AP Monitor Filters (N)
+ // +--------------------+ <-- BackupBufferAddr (CpuMpData->BackupBuffer)
+ // Backup Buffer
+ // +--------------------+
+ // Padding
+ // +--------------------+ <-- ApIdtBase (8-byte boundary)
+ // AP IDT All APs share one separate IDT. So AP can get address of CPU_MP_DATA from IDT Base.
+ // +--------------------+ <-- CpuMpData
+ // CPU_MP_DATA
+ // +--------------------+ <-- CpuMpData->CpuData
+ // CPU_AP_DATA (N)
+ // +--------------------+ <-- CpuMpData->CpuInfoInHob
+ // CPU_INFO_IN_HOB (N)
+ // +--------------------+
+ //
MonitorBuffer = (UINT8 *) (Buffer + ApStackSize * MaxLogicalProcessorNumber);
BackupBufferAddr = (UINTN) MonitorBuffer + MonitorFilterSize * MaxLogicalProcessorNumber;
- CpuMpData = (CPU_MP_DATA *) (BackupBufferAddr + ApResetVectorSize);
+ ApIdtBase = ALIGN_VALUE (BackupBufferAddr + ApResetVectorSize, 8);
+ CpuMpData = (CPU_MP_DATA *) (ApIdtBase + VolatileRegisters.Idtr.Limit + 1);
CpuMpData->Buffer = Buffer;
CpuMpData->CpuApStackSize = ApStackSize;
CpuMpData->BackupBuffer = BackupBufferAddr;
@@ -1471,17 +1682,29 @@ MpInitLibInitialize (
CpuMpData->SwitchBspFlag = FALSE;
CpuMpData->CpuData = (CPU_AP_DATA *) (CpuMpData + 1);
CpuMpData->CpuInfoInHob = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber);
- CpuMpData->MicrocodePatchAddress = PcdGet64 (PcdCpuMicrocodePatchAddress);
- CpuMpData->MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);
InitializeSpinLock(&CpuMpData->MpLock);
+
+ //
+ // Make sure no memory usage outside of the allocated buffer.
//
- // Save BSP's Control registers to APs
+ ASSERT ((CpuMpData->CpuInfoInHob + sizeof (CPU_INFO_IN_HOB) * MaxLogicalProcessorNumber) ==
+ Buffer + BufferSize);
+
+ //
+ // Duplicate BSP's IDT to APs.
+ // All APs share one separate IDT. So AP can get the address of CpuMpData by using IDTR.BASE + IDTR.LIMIT + 1
+ //
+ CopyMem ((VOID *)ApIdtBase, (VOID *)VolatileRegisters.Idtr.Base, VolatileRegisters.Idtr.Limit + 1);
+ VolatileRegisters.Idtr.Base = ApIdtBase;
//
- SaveVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters);
+ // Don't pass BSP's TR to APs to avoid AP init failure.
+ //
+ VolatileRegisters.Tr = 0;
+ CopyMem (&CpuMpData->CpuData[0].VolatileRegisters, &VolatileRegisters, sizeof (VolatileRegisters));
//
// Set BSP basic information
//
- InitializeApData (CpuMpData, 0, 0, CpuMpData->Buffer);
+ InitializeApData (CpuMpData, 0, 0, CpuMpData->Buffer + ApStackSize);
//
// Save assembly code information
//
@@ -1491,6 +1714,9 @@ MpInitLibInitialize (
//
CpuMpData->ApLoopMode = ApLoopMode;
DEBUG ((DEBUG_INFO, "AP Loop Mode is %d\n", CpuMpData->ApLoopMode));
+
+ CpuMpData->WakeUpByInitSipiSipi = (CpuMpData->ApLoopMode == ApInHltLoop);
+
//
// Set up APs wakeup signal buffer
//
@@ -1499,14 +1725,6 @@ MpInitLibInitialize (
(UINT32 *)(MonitorBuffer + MonitorFilterSize * Index);
}
//
- // Load Microcode on BSP
- //
- MicrocodeDetect (CpuMpData);
- //
- // Store BSP's MTRR setting
- //
- MtrrGetAllMtrrs (&CpuMpData->MtrrTable);
- //
// Enable the local APIC for Virtual Wire Mode.
//
ProgramVirtualWireMode ();
@@ -1525,37 +1743,51 @@ MpInitLibInitialize (
//
CpuMpData->CpuCount = OldCpuMpData->CpuCount;
CpuMpData->BspNumber = OldCpuMpData->BspNumber;
- CpuMpData->InitFlag = ApInitReconfig;
CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob;
CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
InitializeSpinLock(&CpuMpData->CpuData[Index].ApLock);
- if (CpuInfoInHob[Index].InitialApicId >= 255 || Index > 254) {
- CpuMpData->X2ApicEnable = TRUE;
- }
CpuMpData->CpuData[Index].CpuHealthy = (CpuInfoInHob[Index].Health == 0)? TRUE:FALSE;
CpuMpData->CpuData[Index].ApFunction = 0;
- CopyMem (
- &CpuMpData->CpuData[Index].VolatileRegisters,
- &CpuMpData->CpuData[0].VolatileRegisters,
- sizeof (CPU_VOLATILE_REGISTERS)
- );
+ CopyMem (&CpuMpData->CpuData[Index].VolatileRegisters, &VolatileRegisters, sizeof (CPU_VOLATILE_REGISTERS));
}
- if (MaxLogicalProcessorNumber > 1) {
- //
- // Wakeup APs to do some AP initialize sync
- //
- WakeUpAP (CpuMpData, TRUE, 0, ApInitializeSync, CpuMpData);
- //
- // Wait for all APs finished initialization
- //
- while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) {
- CpuPause ();
- }
- CpuMpData->InitFlag = ApInitDone;
- for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
- SetApState (&CpuMpData->CpuData[Index], CpuStateIdle);
- }
+ }
+
+ if (!GetMicrocodePatchInfoFromHob (
+ &CpuMpData->MicrocodePatchAddress,
+ &CpuMpData->MicrocodePatchRegionSize
+ )) {
+ //
+ // The microcode patch information cache HOB does not exist, which means
+ // the microcode patches data has not been loaded into memory yet
+ //
+ ShadowMicrocodeUpdatePatch (CpuMpData);
+ }
+
+ //
+ // Detect and apply Microcode on BSP
+ //
+ MicrocodeDetect (CpuMpData, CpuMpData->BspNumber);
+ //
+ // Store BSP's MTRR setting
+ //
+ MtrrGetAllMtrrs (&CpuMpData->MtrrTable);
+
+ //
+ // Wakeup APs to do some AP initialize sync (Microcode & MTRR)
+ //
+ if (CpuMpData->CpuCount > 1) {
+ CpuMpData->InitFlag = ApInitReconfig;
+ WakeUpAP (CpuMpData, TRUE, 0, ApInitializeSync, CpuMpData, TRUE);
+ //
+ // Wait for all APs finished initialization
+ //
+ while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) {
+ CpuPause ();
+ }
+ CpuMpData->InitFlag = ApInitDone;
+ for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
+ SetApState (&CpuMpData->CpuData[Index], CpuStateIdle);
}
}
@@ -1654,7 +1886,7 @@ MpInitLibGetProcessorInfo (
enabled AP. Otherwise, it will be disabled.
@retval EFI_SUCCESS BSP successfully switched.
- @retval others Failed to switch BSP.
+ @retval others Failed to switch BSP.
**/
EFI_STATUS
@@ -1740,7 +1972,7 @@ SwitchBSPWorker (
//
// Need to wakeUp AP (future BSP).
//
- WakeUpAP (CpuMpData, FALSE, ProcessorNumber, FutureBSPProc, CpuMpData);
+ WakeUpAP (CpuMpData, FALSE, ProcessorNumber, FutureBSPProc, CpuMpData, TRUE);
AsmExchangeRole (&CpuMpData->BSPInfo, &CpuMpData->APInfo);
@@ -1750,6 +1982,7 @@ SwitchBSPWorker (
ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);
ApicBaseMsr.Bits.BSP = 1;
AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);
+ ProgramVirtualWireMode ();
//
// Wait for old BSP finished AP task
@@ -1950,6 +2183,7 @@ MpInitLibGetNumberOfProcessors (
number. If FALSE, then all the enabled APs
execute the function specified by Procedure
simultaneously.
+ @param[in] ExcludeBsp Whether let BSP also trig this task.
@param[in] WaitEvent The event created by the caller with CreateEvent()
service.
@param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
@@ -1971,9 +2205,10 @@ MpInitLibGetNumberOfProcessors (
**/
EFI_STATUS
-StartupAllAPsWorker (
+StartupAllCPUsWorker (
IN EFI_AP_PROCEDURE Procedure,
IN BOOLEAN SingleThread,
+ IN BOOLEAN ExcludeBsp,
IN EFI_EVENT WaitEvent OPTIONAL,
IN UINTN TimeoutInMicroseconds,
IN VOID *ProcedureArgument OPTIONAL,
@@ -1995,7 +2230,7 @@ StartupAllAPsWorker (
*FailedCpuList = NULL;
}
- if (CpuMpData->CpuCount == 1) {
+ if (CpuMpData->CpuCount == 1 && ExcludeBsp) {
return EFI_NOT_STARTED;
}
@@ -2038,14 +2273,14 @@ StartupAllAPsWorker (
}
}
- if (!HasEnabledAp) {
+ if (!HasEnabledAp && ExcludeBsp) {
//
- // If no enabled AP exists, return EFI_NOT_STARTED.
+ // If no enabled AP exists and not include Bsp to do the procedure, return EFI_NOT_STARTED.
//
return EFI_NOT_STARTED;
}
- CpuMpData->StartCount = 0;
+ CpuMpData->RunningCount = 0;
for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) {
CpuData = &CpuMpData->CpuData[ProcessorNumber];
CpuData->Waiting = FALSE;
@@ -2055,7 +2290,7 @@ StartupAllAPsWorker (
// Mark this processor as responsible for current calling.
//
CpuData->Waiting = TRUE;
- CpuMpData->StartCount++;
+ CpuMpData->RunningCount++;
}
}
}
@@ -2064,7 +2299,6 @@ StartupAllAPsWorker (
CpuMpData->ProcArguments = ProcedureArgument;
CpuMpData->SingleThread = SingleThread;
CpuMpData->FinishedCount = 0;
- CpuMpData->RunningCount = 0;
CpuMpData->FailedCpuList = FailedCpuList;
CpuMpData->ExpectedTime = CalculateTimeout (
TimeoutInMicroseconds,
@@ -2074,19 +2308,26 @@ StartupAllAPsWorker (
CpuMpData->WaitEvent = WaitEvent;
if (!SingleThread) {
- WakeUpAP (CpuMpData, TRUE, 0, Procedure, ProcedureArgument);
+ WakeUpAP (CpuMpData, TRUE, 0, Procedure, ProcedureArgument, FALSE);
} else {
for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) {
if (ProcessorNumber == CallerNumber) {
continue;
}
if (CpuMpData->CpuData[ProcessorNumber].Waiting) {
- WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument);
+ WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument, TRUE);
break;
}
}
}
+ if (!ExcludeBsp) {
+ //
+ // Start BSP.
+ //
+ Procedure (ProcedureArgument);
+ }
+
Status = EFI_SUCCESS;
if (WaitEvent == NULL) {
do {
@@ -2193,7 +2434,7 @@ StartupThisAPWorker (
CpuData->ExpectedTime = CalculateTimeout (TimeoutInMicroseconds, &CpuData->CurrentTime);
CpuData->TotalTime = 0;
- WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument);
+ WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument, TRUE);
//
// If WaitEvent is NULL, execute in blocking mode.
@@ -2232,3 +2473,47 @@ GetCpuMpDataFromGuidedHob (
return CpuMpData;
}
+/**
+ This service executes a caller provided function on all enabled CPUs.
+
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system. See type
+ EFI_AP_PROCEDURE.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode. Zero means
+ infinity. TimeoutInMicroseconds is ignored
+ for BSP.
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+
+ @retval EFI_SUCCESS In blocking mode, all CPUs have finished before
+ the timeout expired.
+ @retval EFI_SUCCESS In non-blocking mode, function has been dispatched
+ to all enabled CPUs.
+ @retval EFI_DEVICE_ERROR Caller processor is AP.
+ @retval EFI_NOT_READY Any enabled APs are busy.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+ @retval EFI_TIMEOUT In blocking mode, the timeout expired before
+ all enabled APs have finished.
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+MpInitLibStartupAllCPUs (
+ IN EFI_AP_PROCEDURE Procedure,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL
+ )
+{
+ return StartupAllCPUsWorker (
+ Procedure,
+ FALSE,
+ FALSE,
+ NULL,
+ TimeoutInMicroseconds,
+ ProcedureArgument,
+ NULL
+ );
+}