/** @file\r
CPU MP Initialize Library common functions.\r
\r
- Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>\r
- This program and the accompanying materials\r
- are licensed and made available under the terms and conditions of the BSD License\r
- which accompanies this distribution. The full text of the license may be found at\r
- http://opensource.org/licenses/bsd-license.php\r
-\r
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
- WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+ Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
)\r
{\r
UINTN Index;\r
+ CPU_INFO_IN_HOB *CpuInfoInHob;\r
+ BOOLEAN X2Apic;\r
\r
//\r
// Send 1st broadcast IPI to APs to wakeup APs\r
//\r
- CpuMpData->InitFlag = ApInitConfig;\r
- CpuMpData->X2ApicEnable = FALSE;\r
+ CpuMpData->InitFlag = ApInitConfig;\r
WakeUpAP (CpuMpData, TRUE, 0, NULL, NULL, TRUE);\r
CpuMpData->InitFlag = ApInitDone;\r
ASSERT (CpuMpData->CpuCount <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber));\r
CpuPause ();\r
}\r
\r
+\r
+ //\r
+ // Enable x2APIC mode if\r
+ // 1. Number of CPU is greater than 255; or\r
+ // 2. There are any logical processors reporting an Initial APIC ID of 255 or greater.\r
+ //\r
+ X2Apic = FALSE;\r
if (CpuMpData->CpuCount > 255) {\r
//\r
// If there are more than 255 processor found, force to enable X2APIC\r
//\r
- CpuMpData->X2ApicEnable = TRUE;\r
+ X2Apic = TRUE;\r
+ } else {\r
+ CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
+ for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
+ if (CpuInfoInHob[Index].InitialApicId >= 0xFF) {\r
+ X2Apic = TRUE;\r
+ break;\r
+ }\r
+ }\r
}\r
- if (CpuMpData->X2ApicEnable) {\r
+\r
+ if (X2Apic) {\r
DEBUG ((DEBUG_INFO, "Force x2APIC mode!\n"));\r
//\r
// Wakeup all APs to enable x2APIC mode\r
IN UINT64 ApTopOfStack\r
)\r
{\r
- CPU_INFO_IN_HOB *CpuInfoInHob;\r
+ CPU_INFO_IN_HOB *CpuInfoInHob;\r
+ MSR_IA32_PLATFORM_ID_REGISTER PlatformIdMsr;\r
\r
CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId ();\r
\r
CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE;\r
CpuMpData->CpuData[ProcessorNumber].CpuHealthy = (BistData == 0) ? TRUE : FALSE;\r
- if (CpuInfoInHob[ProcessorNumber].InitialApicId >= 0xFF) {\r
- //\r
- // Set x2APIC mode if there are any logical processor reporting\r
- // an Initial APIC ID of 255 or greater.\r
- //\r
- AcquireSpinLock(&CpuMpData->MpLock);\r
- CpuMpData->X2ApicEnable = TRUE;\r
- ReleaseSpinLock(&CpuMpData->MpLock);\r
- }\r
+\r
+ PlatformIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID);\r
+ CpuMpData->CpuData[ProcessorNumber].PlatformId = (UINT8) PlatformIdMsr.Bits.PlatformId;\r
+\r
+ AsmCpuid (\r
+ CPUID_VERSION_INFO,\r
+ &CpuMpData->CpuData[ProcessorNumber].ProcessorSignature,\r
+ NULL,\r
+ NULL,\r
+ NULL\r
+ );\r
\r
InitializeSpinLock(&CpuMpData->CpuData[ProcessorNumber].ApLock);\r
SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);\r
RestoreVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters, FALSE);\r
InitializeApData (CpuMpData, ProcessorNumber, BistData, ApTopOfStack);\r
ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal;\r
+\r
+ InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);\r
} else {\r
//\r
// Execute AP function if AP is ready\r
}\r
}\r
}\r
- SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);\r
+ SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateFinished);\r
}\r
}\r
\r
// AP finished executing C code\r
//\r
InterlockedIncrement ((UINT32 *) &CpuMpData->FinishedCount);\r
- InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);\r
\r
//\r
// Place AP is specified loop mode\r
volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;\r
UINTN Size;\r
IA32_SEGMENT_DESCRIPTOR *Selector;\r
+ IA32_CR4 Cr4;\r
\r
ExchangeInfo = CpuMpData->MpCpuExchangeInfo;\r
ExchangeInfo->Lock = 0;\r
\r
ExchangeInfo->InitializeFloatingPointUnitsAddress = (UINTN)InitializeFloatingPointUnits;\r
\r
+ //\r
+ // We can check either CPUID(7).ECX[bit16] or check CR4.LA57[bit12]\r
+ // to determin whether 5-Level Paging is enabled.\r
+ // CPUID(7).ECX[bit16] shows CPU's capability, CR4.LA57[bit12] shows\r
+ // current system setting.\r
+ // Using latter way is simpler because it also eliminates the needs to\r
+ // check whether platform wants to enable it.\r
+ //\r
+ Cr4.UintN = AsmReadCr4 ();\r
+ ExchangeInfo->Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);\r
+ DEBUG ((DEBUG_INFO, "%a: 5-Level Paging = %d\n", gEfiCallerBaseName, ExchangeInfo->Enable5LevelPaging));\r
+\r
//\r
// Get the BSP's data of GDT and IDT\r
//\r
SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart);\r
}\r
if (CpuMpData->InitFlag == ApInitConfig) {\r
- //\r
- // Here support two methods to collect AP count through adjust\r
- // PcdCpuApInitTimeOutInMicroSeconds values.\r
- //\r
- // one way is set a value to just let the first AP to start the\r
- // initialization, then through the later while loop to wait all Aps\r
- // finsh the initialization.\r
- // The other way is set a value to let all APs finished the initialzation.\r
- // In this case, the later while loop is useless.\r
- //\r
- TimedWaitForApFinish (\r
- CpuMpData,\r
- PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1,\r
- PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds)\r
- );\r
+ if (PcdGet32 (PcdCpuBootLogicalProcessorNumber) > 0) {\r
+ //\r
+ // The AP enumeration algorithm below is suitable only when the\r
+ // platform can tell us the *exact* boot CPU count in advance.\r
+ //\r
+ // The wait below finishes only when the detected AP count reaches\r
+ // (PcdCpuBootLogicalProcessorNumber - 1), regardless of how long that\r
+ // takes. If at least one AP fails to check in (meaning a platform\r
+ // hardware bug), the detection hangs forever, by design. If the actual\r
+ // boot CPU count in the system is higher than\r
+ // PcdCpuBootLogicalProcessorNumber (meaning a platform\r
+ // misconfiguration), then some APs may complete initialization after\r
+ // the wait finishes, and cause undefined behavior.\r
+ //\r
+ TimedWaitForApFinish (\r
+ CpuMpData,\r
+ PcdGet32 (PcdCpuBootLogicalProcessorNumber) - 1,\r
+ MAX_UINT32 // approx. 71 minutes\r
+ );\r
+ } else {\r
+ //\r
+ // The AP enumeration algorithm below is suitable for two use cases.\r
+ //\r
+ // (1) The check-in time for an individual AP is bounded, and APs run\r
+ // through their initialization routines strongly concurrently. In\r
+ // particular, the number of concurrently running APs\r
+ // ("NumApsExecuting") is never expected to fall to zero\r
+ // *temporarily* -- it is expected to fall to zero only when all\r
+ // APs have checked-in.\r
+ //\r
+ // In this case, the platform is supposed to set\r
+ // PcdCpuApInitTimeOutInMicroSeconds to a low-ish value (just long\r
+ // enough for one AP to start initialization). The timeout will be\r
+ // reached soon, and remaining APs are collected by watching\r
+ // NumApsExecuting fall to zero. If NumApsExecuting falls to zero\r
+ // mid-process, while some APs have not completed initialization,\r
+ // the behavior is undefined.\r
+ //\r
+ // (2) The check-in time for an individual AP is unbounded, and/or APs\r
+ // may complete their initializations widely spread out. In\r
+ // particular, some APs may finish initialization before some APs\r
+ // even start.\r
+ //\r
+ // In this case, the platform is supposed to set\r
+ // PcdCpuApInitTimeOutInMicroSeconds to a high-ish value. The AP\r
+ // enumeration will always take that long (except when the boot CPU\r
+ // count happens to be maximal, that is,\r
+ // PcdCpuMaxLogicalProcessorNumber). All APs are expected to\r
+ // check-in before the timeout, and NumApsExecuting is assumed zero\r
+ // at timeout. APs that miss the time-out may cause undefined\r
+ // behavior.\r
+ //\r
+ TimedWaitForApFinish (\r
+ CpuMpData,\r
+ PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1,\r
+ PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds)\r
+ );\r
\r
- while (CpuMpData->MpCpuExchangeInfo->NumApsExecuting != 0) {\r
- CpuPause();\r
+ while (CpuMpData->MpCpuExchangeInfo->NumApsExecuting != 0) {\r
+ CpuPause();\r
+ }\r
}\r
} else {\r
//\r
//\r
// If the AP finishes for StartupThisAP(), return EFI_SUCCESS.\r
//\r
- if (GetApState(CpuData) == CpuStateIdle) {\r
+ if (GetApState(CpuData) == CpuStateFinished) {\r
if (CpuData->Finished != NULL) {\r
*(CpuData->Finished) = TRUE;\r
}\r
+ SetApState (CpuData, CpuStateIdle);\r
return EFI_SUCCESS;\r
} else {\r
//\r
// Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the\r
// value of state after setting the it to CpuStateIdle, so BSP can safely make use of its value.\r
//\r
- if (GetApState(CpuData) == CpuStateIdle) {\r
+ if (GetApState(CpuData) == CpuStateFinished) {\r
CpuMpData->RunningCount --;\r
CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE;\r
+ SetApState(CpuData, CpuStateIdle);\r
\r
//\r
// If in Single Thread mode, then search for the next waiting AP for execution.\r
CpuMpData->SwitchBspFlag = FALSE;\r
CpuMpData->CpuData = (CPU_AP_DATA *) (CpuMpData + 1);\r
CpuMpData->CpuInfoInHob = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber);\r
- CpuMpData->MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);\r
- //\r
- // If platform has more than one CPU, relocate microcode to memory to reduce\r
- // loading microcode time.\r
- //\r
- MicrocodePatchInRam = NULL;\r
- if (MaxLogicalProcessorNumber > 1) {\r
- MicrocodePatchInRam = AllocatePages (\r
- EFI_SIZE_TO_PAGES (\r
- (UINTN)CpuMpData->MicrocodePatchRegionSize\r
- )\r
- );\r
- }\r
- if (MicrocodePatchInRam == NULL) {\r
- //\r
- // there is only one processor, or no microcode patch is available, or\r
- // memory allocation failed\r
- //\r
- CpuMpData->MicrocodePatchAddress = PcdGet64 (PcdCpuMicrocodePatchAddress);\r
- } else {\r
+ if (OldCpuMpData == NULL) {\r
+ CpuMpData->MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);\r
//\r
- // there are multiple processors, and a microcode patch is available, and\r
- // memory allocation succeeded\r
+ // If platform has more than one CPU, relocate microcode to memory to reduce\r
+ // loading microcode time.\r
//\r
- CopyMem (\r
- MicrocodePatchInRam,\r
- (VOID *)(UINTN)PcdGet64 (PcdCpuMicrocodePatchAddress),\r
- (UINTN)CpuMpData->MicrocodePatchRegionSize\r
- );\r
- CpuMpData->MicrocodePatchAddress = (UINTN)MicrocodePatchInRam;\r
+ MicrocodePatchInRam = NULL;\r
+ if (MaxLogicalProcessorNumber > 1) {\r
+ MicrocodePatchInRam = AllocatePages (\r
+ EFI_SIZE_TO_PAGES (\r
+ (UINTN)CpuMpData->MicrocodePatchRegionSize\r
+ )\r
+ );\r
+ }\r
+ if (MicrocodePatchInRam == NULL) {\r
+ //\r
+ // there is only one processor, or no microcode patch is available, or\r
+ // memory allocation failed\r
+ //\r
+ CpuMpData->MicrocodePatchAddress = PcdGet64 (PcdCpuMicrocodePatchAddress);\r
+ } else {\r
+ //\r
+ // there are multiple processors, and a microcode patch is available, and\r
+ // memory allocation succeeded\r
+ //\r
+ CopyMem (\r
+ MicrocodePatchInRam,\r
+ (VOID *)(UINTN)PcdGet64 (PcdCpuMicrocodePatchAddress),\r
+ (UINTN)CpuMpData->MicrocodePatchRegionSize\r
+ );\r
+ CpuMpData->MicrocodePatchAddress = (UINTN)MicrocodePatchInRam;\r
+ }\r
+ }else {\r
+ CpuMpData->MicrocodePatchRegionSize = OldCpuMpData->MicrocodePatchRegionSize;\r
+ CpuMpData->MicrocodePatchAddress = OldCpuMpData->MicrocodePatchAddress;\r
}\r
-\r
InitializeSpinLock(&CpuMpData->MpLock);\r
\r
//\r
CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
InitializeSpinLock(&CpuMpData->CpuData[Index].ApLock);\r
- if (CpuInfoInHob[Index].InitialApicId >= 255 || Index > 254) {\r
- CpuMpData->X2ApicEnable = TRUE;\r
- }\r
CpuMpData->CpuData[Index].CpuHealthy = (CpuInfoInHob[Index].Health == 0)? TRUE:FALSE;\r
CpuMpData->CpuData[Index].ApFunction = 0;\r
CopyMem (&CpuMpData->CpuData[Index].VolatileRegisters, &VolatileRegisters, sizeof (CPU_VOLATILE_REGISTERS));\r
//\r
// Wait for old BSP finished AP task\r
//\r
- while (GetApState (&CpuMpData->CpuData[CallerNumber]) != CpuStateIdle) {\r
+ while (GetApState (&CpuMpData->CpuData[CallerNumber]) != CpuStateFinished) {\r
CpuPause ();\r
}\r
\r
number. If FALSE, then all the enabled APs\r
execute the function specified by Procedure\r
simultaneously.\r
+ @param[in] ExcludeBsp Whether let BSP also trig this task.\r
@param[in] WaitEvent The event created by the caller with CreateEvent()\r
service.\r
@param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
\r
**/\r
EFI_STATUS\r
-StartupAllAPsWorker (\r
+StartupAllCPUsWorker (\r
IN EFI_AP_PROCEDURE Procedure,\r
IN BOOLEAN SingleThread,\r
+ IN BOOLEAN ExcludeBsp,\r
IN EFI_EVENT WaitEvent OPTIONAL,\r
IN UINTN TimeoutInMicroseconds,\r
IN VOID *ProcedureArgument OPTIONAL,\r
*FailedCpuList = NULL;\r
}\r
\r
- if (CpuMpData->CpuCount == 1) {\r
+ if (CpuMpData->CpuCount == 1 && ExcludeBsp) {\r
return EFI_NOT_STARTED;\r
}\r
\r
}\r
}\r
\r
- if (!HasEnabledAp) {\r
+ if (!HasEnabledAp && ExcludeBsp) {\r
//\r
- // If no enabled AP exists, return EFI_NOT_STARTED.\r
+ // If no enabled AP exists and not include Bsp to do the procedure, return EFI_NOT_STARTED.\r
//\r
return EFI_NOT_STARTED;\r
}\r
}\r
}\r
\r
+ if (!ExcludeBsp) {\r
+ //\r
+ // Start BSP.\r
+ //\r
+ Procedure (ProcedureArgument);\r
+ }\r
+\r
Status = EFI_SUCCESS;\r
if (WaitEvent == NULL) {\r
do {\r
return CpuMpData;\r
}\r
\r
+/**\r
+ This service executes a caller provided function on all enabled CPUs.\r
+\r
+ @param[in] Procedure A pointer to the function to be run on\r
+ enabled APs of the system. See type\r
+ EFI_AP_PROCEDURE.\r
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
+ APs to return from Procedure, either for\r
+ blocking or non-blocking mode. Zero means\r
+ infinity. TimeoutInMicroseconds is ignored\r
+ for BSP.\r
+ @param[in] ProcedureArgument The parameter passed into Procedure for\r
+ all APs.\r
+\r
+ @retval EFI_SUCCESS In blocking mode, all CPUs have finished before\r
+ the timeout expired.\r
+ @retval EFI_SUCCESS In non-blocking mode, function has been dispatched\r
+ to all enabled CPUs.\r
+ @retval EFI_DEVICE_ERROR Caller processor is AP.\r
+ @retval EFI_NOT_READY Any enabled APs are busy.\r
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.\r
+ @retval EFI_TIMEOUT In blocking mode, the timeout expired before\r
+ all enabled APs have finished.\r
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+MpInitLibStartupAllCPUs (\r
+ IN EFI_AP_PROCEDURE Procedure,\r
+ IN UINTN TimeoutInMicroseconds,\r
+ IN VOID *ProcedureArgument OPTIONAL\r
+ )\r
+{\r
+ return StartupAllCPUsWorker (\r
+ Procedure,\r
+ FALSE,\r
+ FALSE,\r
+ NULL,\r
+ TimeoutInMicroseconds,\r
+ ProcedureArgument,\r
+ NULL\r
+ );\r
+}\r