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 + ); +}