X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=UefiCpuPkg%2FLibrary%2FMpInitLib%2FMpLib.c;h=64fddb497e1e7f59c93a95e56690cfe7719c408d;hb=b95908e04317a2b1e3641845ba36f673d5aebada;hp=78d18b616eb5e954feae419787adfd9530da6372;hpb=af8ba51aca4e0b41a359fe467fb5c5b9baa75a05;p=mirror_edk2.git diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c index 78d18b616e..64fddb497e 100644 --- a/UefiCpuPkg/Library/MpInitLib/MpLib.c +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c @@ -1,25 +1,27 @@ /** @file CPU MP Initialize Library common functions. - Copyright (c) 2016, 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 + Copyright (c) 2016 - 2021, Intel Corporation. All rights reserved.
+ Copyright (c) 2020, AMD Inc. All rights reserved.
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "MpLib.h" +#include +#include +#include -EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID; +EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID; /** The function will check if BSP Execute Disable is enabled. - DxeIpl may have enabled Execute Disable for BSP, - APs need to get the status and sync up the settings. + + DxeIpl may have enabled Execute Disable for BSP, APs need to + get the status and sync up the settings. + If BSP's CR0.Paging is not set, BSP execute Disble feature is + not working actually. @retval TRUE BSP Execute Disable is enabled. @retval FALSE BSP Execute Disable is not enabled. @@ -33,23 +35,30 @@ IsBspExecuteDisableEnabled ( CPUID_EXTENDED_CPU_SIG_EDX Edx; MSR_IA32_EFER_REGISTER EferMsr; BOOLEAN Enabled; + IA32_CR0 Cr0; - Enabled = FALSE; - AsmCpuid (CPUID_EXTENDED_FUNCTION, &Eax, NULL, NULL, NULL); - if (Eax >= CPUID_EXTENDED_CPU_SIG) { - AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &Edx.Uint32); + Enabled = FALSE; + Cr0.UintN = AsmReadCr0 (); + if (Cr0.Bits.PG != 0) { // - // CPUID 0x80000001 - // Bit 20: Execute Disable Bit available. + // If CR0 Paging bit is set // - if (Edx.Bits.NX != 0) { - EferMsr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER); + AsmCpuid (CPUID_EXTENDED_FUNCTION, &Eax, NULL, NULL, NULL); + if (Eax >= CPUID_EXTENDED_CPU_SIG) { + AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &Edx.Uint32); // - // MSR 0xC0000080 - // Bit 11: Execute Disable Bit enable. + // CPUID 0x80000001 + // Bit 20: Execute Disable Bit available. // - if (EferMsr.Bits.NXE != 0) { - Enabled = TRUE; + if (Edx.Bits.NX != 0) { + EferMsr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER); + // + // MSR 0xC0000080 + // Bit 11: Execute Disable Bit enable. + // + if (EferMsr.Bits.NXE != 0) { + Enabled = TRUE; + } } } } @@ -68,12 +77,12 @@ IsBspExecuteDisableEnabled ( VOID EFIAPI FutureBSPProc ( - IN VOID *Buffer + IN VOID *Buffer ) { - CPU_MP_DATA *DataInHob; + CPU_MP_DATA *DataInHob; - DataInHob = (CPU_MP_DATA *) Buffer; + DataInHob = (CPU_MP_DATA *)Buffer; AsmExchangeRole (&DataInHob->APInfo, &DataInHob->BSPInfo); } @@ -86,7 +95,7 @@ FutureBSPProc ( **/ CPU_STATE GetApState ( - IN CPU_AP_DATA *CpuData + IN CPU_AP_DATA *CpuData ) { return CpuData->State; @@ -100,8 +109,8 @@ GetApState ( **/ VOID SetApState ( - IN CPU_AP_DATA *CpuData, - IN CPU_STATE State + IN CPU_AP_DATA *CpuData, + IN CPU_STATE State ) { AcquireSpinLock (&CpuData->ApLock); @@ -110,13 +119,13 @@ SetApState ( } /** - Save BSP's local APIC timer setting + Save BSP's local APIC timer setting. @param[in] CpuMpData Pointer to CPU MP Data **/ VOID SaveLocalApicTimerSetting ( - IN CPU_MP_DATA *CpuMpData + IN CPU_MP_DATA *CpuMpData ) { // @@ -138,7 +147,7 @@ SaveLocalApicTimerSetting ( **/ VOID SyncLocalApicTimerSetting ( - IN CPU_MP_DATA *CpuMpData + IN CPU_MP_DATA *CpuMpData ) { // @@ -163,10 +172,10 @@ SyncLocalApicTimerSetting ( **/ VOID SaveVolatileRegisters ( - OUT CPU_VOLATILE_REGISTERS *VolatileRegisters + OUT CPU_VOLATILE_REGISTERS *VolatileRegisters ) { - CPUID_VERSION_INFO_EDX VersionInfoEdx; + CPUID_VERSION_INFO_EDX VersionInfoEdx; VolatileRegisters->Cr0 = AsmReadCr0 (); VolatileRegisters->Cr3 = AsmReadCr3 (); @@ -185,6 +194,10 @@ SaveVolatileRegisters ( VolatileRegisters->Dr6 = AsmReadDr6 (); VolatileRegisters->Dr7 = AsmReadDr7 (); } + + AsmReadGdtr (&VolatileRegisters->Gdtr); + AsmReadIdtr (&VolatileRegisters->Idtr); + VolatileRegisters->Tr = AsmReadTr (); } /** @@ -196,15 +209,16 @@ SaveVolatileRegisters ( **/ VOID RestoreVolatileRegisters ( - IN CPU_VOLATILE_REGISTERS *VolatileRegisters, - IN BOOLEAN IsRestoreDr + IN CPU_VOLATILE_REGISTERS *VolatileRegisters, + IN BOOLEAN IsRestoreDr ) { - CPUID_VERSION_INFO_EDX VersionInfoEdx; + 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); @@ -221,6 +235,19 @@ 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); + } + } } /** @@ -234,7 +261,7 @@ IsMwaitSupport ( VOID ) { - CPUID_VERSION_INFO_ECX VersionInfoEcx; + CPUID_VERSION_INFO_ECX VersionInfoEcx; AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, &VersionInfoEcx.Uint32, NULL); return (VersionInfoEcx.Bits.MONITOR == 1) ? TRUE : FALSE; @@ -249,11 +276,11 @@ IsMwaitSupport ( **/ UINT8 GetApLoopMode ( - OUT UINT32 *MonitorFilterSize + OUT UINT32 *MonitorFilterSize ) { - UINT8 ApLoopMode; - CPUID_MONITOR_MWAIT_EBX MonitorMwaitEbx; + UINT8 ApLoopMode; + CPUID_MONITOR_MWAIT_EBX MonitorMwaitEbx; ASSERT (MonitorFilterSize != NULL); @@ -267,6 +294,14 @@ GetApLoopMode ( // ApLoopMode = ApInHltLoop; } + + if (ConfidentialComputingGuestHas (CCAttrAmdSevEs)) { + // + // For SEV-ES, force AP in Hlt-loop mode in order to use the GHCB + // protocol for starting APs + // + ApLoopMode = ApInHltLoop; + } } if (ApLoopMode != ApInMwaitLoop) { @@ -293,19 +328,20 @@ GetApLoopMode ( **/ VOID SortApicId ( - IN CPU_MP_DATA *CpuMpData + IN CPU_MP_DATA *CpuMpData ) { - UINTN Index1; - UINTN Index2; - UINTN Index3; - UINT32 ApicId; - CPU_INFO_IN_HOB CpuInfo; - UINT32 ApCount; - CPU_INFO_IN_HOB *CpuInfoInHob; - - ApCount = CpuMpData->CpuCount - 1; - CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + UINTN Index1; + UINTN Index2; + UINTN Index3; + UINT32 ApicId; + 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; if (ApCount != 0) { for (Index1 = 0; Index1 < ApCount; Index1++) { Index3 = Index1; @@ -319,6 +355,7 @@ SortApicId ( ApicId = CpuInfoInHob[Index2].ApicId; } } + if (Index3 != Index1) { CopyMem (&CpuInfo, &CpuInfoInHob[Index3], sizeof (CPU_INFO_IN_HOB)); CopyMem ( @@ -327,6 +364,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; } } @@ -336,7 +381,7 @@ SortApicId ( ApicId = GetInitialApicId (); for (Index1 = 0; Index1 < CpuMpData->CpuCount; Index1++) { if (CpuInfoInHob[Index1].ApicId == ApicId) { - CpuMpData->BspNumber = (UINT32) Index1; + CpuMpData->BspNumber = (UINT32)Index1; break; } } @@ -369,16 +414,20 @@ ApInitializeSync ( ) { CPU_MP_DATA *CpuMpData; + UINTN ProcessorNumber; + EFI_STATUS Status; - CpuMpData = (CPU_MP_DATA *) Buffer; + CpuMpData = (CPU_MP_DATA *)Buffer; + Status = GetProcessorNumber (CpuMpData, &ProcessorNumber); + ASSERT_EFI_ERROR (Status); // - // Sync BSP's MTRR table to AP + // Load microcode on AP // - MtrrSetAllMtrrs (&CpuMpData->MtrrTable); + MicrocodeDetect (CpuMpData, ProcessorNumber); // - // Load microcode on AP + // Sync BSP's MTRR table to AP // - MicrocodeDetect (CpuMpData); + MtrrSetAllMtrrs (&CpuMpData->MtrrTable); } /** @@ -392,23 +441,26 @@ ApInitializeSync ( **/ EFI_STATUS GetProcessorNumber ( - IN CPU_MP_DATA *CpuMpData, - OUT UINTN *ProcessorNumber + IN CPU_MP_DATA *CpuMpData, + OUT UINTN *ProcessorNumber ) { - UINTN TotalProcessorNumber; - UINTN Index; - CPU_INFO_IN_HOB *CpuInfoInHob; + UINTN TotalProcessorNumber; + UINTN Index; + CPU_INFO_IN_HOB *CpuInfoInHob; + UINT32 CurrentApicId; - CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; TotalProcessorNumber = CpuMpData->CpuCount; - for (Index = 0; Index < TotalProcessorNumber; Index ++) { - if (CpuInfoInHob[Index].ApicId == GetApicId ()) { + CurrentApicId = GetApicId (); + for (Index = 0; Index < TotalProcessorNumber; Index++) { + if (CpuInfoInHob[Index].ApicId == CurrentApicId) { *ProcessorNumber = Index; return EFI_SUCCESS; } } + return EFI_NOT_FOUND; } @@ -421,41 +473,72 @@ GetProcessorNumber ( **/ UINTN CollectProcessorCount ( - IN CPU_MP_DATA *CpuMpData + IN CPU_MP_DATA *CpuMpData ) { + 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; + // + // When InitFlag == ApInitConfig, WakeUpAP () guarantees all APs are checked in. + // FinishedCount is the number of check-in APs. + // + CpuMpData->CpuCount = CpuMpData->FinishedCount + 1; ASSERT (CpuMpData->CpuCount <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber)); + // - // Wait for all APs finished the initialization + // 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. // - while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) { - CpuPause (); + X2Apic = FALSE; + if (CpuMpData->CpuCount > 255) { + // + // If there are more than 255 processor found, force to enable X2APIC + // + 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 // while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) { CpuPause (); } + // // Enable x2APIC on BSP // SetApicMode (LOCAL_APIC_MODE_X2APIC); + // + // Set BSP/Aps state to IDLE + // + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + SetApState (&CpuMpData->CpuData[Index], CpuStateIdle); + } } + DEBUG ((DEBUG_INFO, "APIC MODE is %d\n", GetApicMode ())); // // Sort BSP/Aps by CPU APIC ID in ascending order @@ -478,15 +561,16 @@ CollectProcessorCount ( **/ VOID InitializeApData ( - IN OUT CPU_MP_DATA *CpuMpData, - IN UINTN ProcessorNumber, - IN UINT32 BistData, - IN UINT64 ApTopOfStack + IN OUT CPU_MP_DATA *CpuMpData, + IN UINTN ProcessorNumber, + IN UINT32 BistData, + 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 = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId (); CpuInfoInHob[ProcessorNumber].ApicId = GetApicId (); CpuInfoInHob[ProcessorNumber].Health = BistData; @@ -494,17 +578,24 @@ 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); + + // + // NOTE: PlatformId is not relevant on AMD platforms. + // + if (!StandardSignatureIsAuthenticAMD ()) { + PlatformIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID); + CpuMpData->CpuData[ProcessorNumber].PlatformId = (UINT8)PlatformIdMsr.Bits.PlatformId; } - InitializeSpinLock(&CpuMpData->CpuData[ProcessorNumber].ApLock); + AsmCpuid ( + CPUID_VERSION_INFO, + &CpuMpData->CpuData[ProcessorNumber].ProcessorSignature, + NULL, + NULL, + NULL + ); + + InitializeSpinLock (&CpuMpData->CpuData[ProcessorNumber].ApLock); SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); } @@ -512,23 +603,24 @@ 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 ApWakeupFunction ( - IN MP_CPU_EXCHANGE_INFO *ExchangeInfo, - IN UINTN NumApsExecuting + IN MP_CPU_EXCHANGE_INFO *ExchangeInfo, + IN UINTN ApIndex ) { - CPU_MP_DATA *CpuMpData; - UINTN ProcessorNumber; - EFI_AP_PROCEDURE Procedure; - VOID *Parameter; - UINT32 BistData; - volatile UINT32 *ApStartupSignalBuffer; - CPU_INFO_IN_HOB *CpuInfoInHob; - UINT64 ApTopOfStack; + CPU_MP_DATA *CpuMpData; + UINTN ProcessorNumber; + EFI_AP_PROCEDURE Procedure; + VOID *Parameter; + UINT32 BistData; + volatile UINT32 *ApStartupSignalBuffer; + CPU_INFO_IN_HOB *CpuInfoInHob; + UINT64 ApTopOfStack; + UINTN CurrentApicMode; // // AP finished assembly code and begin to execute C code @@ -540,26 +632,25 @@ 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 (); while (TRUE) { if (CpuMpData->InitFlag == ApInitConfig) { - // - // Add CPU number - // - InterlockedIncrement ((UINT32 *) &CpuMpData->CpuCount); - ProcessorNumber = NumApsExecuting; + ProcessorNumber = ApIndex; // // This is first time AP wakeup, get BIST information from AP stack // - ApTopOfStack = CpuMpData->Buffer + (ProcessorNumber + 1) * CpuMpData->CpuApStackSize; - BistData = *(UINT32 *) ((UINTN) ApTopOfStack - sizeof (UINTN)); + 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); @@ -574,81 +665,135 @@ ApWakeupFunction ( // ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; InterlockedCompareExchange32 ( - (UINT32 *) ApStartupSignalBuffer, + (UINT32 *)ApStartupSignalBuffer, WAKEUP_AP_SIGNAL, 0 ); - if (CpuMpData->ApLoopMode == ApInHltLoop) { + + if (CpuMpData->InitFlag == ApInitReconfig) { // - // Restore AP's volatile registers saved + // ApInitReconfig happens when: + // 1. AP is re-enabled after it's disabled, in either PEI or DXE phase. + // 2. AP is initialized in DXE phase. + // In either case, use the volatile registers value derived from BSP. + // NOTE: IDTR.BASE stored in CpuMpData->CpuData[0].VolatileRegisters points to a + // different IDT shared by all APs. // - RestoreVolatileRegisters (&CpuMpData->CpuData[ProcessorNumber].VolatileRegisters, TRUE); + RestoreVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters, FALSE); + } else { + if (CpuMpData->ApLoopMode == ApInHltLoop) { + // + // Restore AP's volatile registers saved before AP is halted + // + 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) { Procedure = (EFI_AP_PROCEDURE)CpuMpData->CpuData[ProcessorNumber].ApFunction; - Parameter = (VOID *) CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument; + Parameter = (VOID *)CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument; if (Procedure != NULL) { SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateBusy); // // Enable source debugging on AP function - // + // EnableDebugAgent (); // // Invoke AP function here // Procedure (Parameter); - CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; if (CpuMpData->SwitchBspFlag) { // // Re-get the processor number due to BSP/AP maybe exchange in AP function // GetProcessorNumber (CpuMpData, &ProcessorNumber); - CpuMpData->CpuData[ProcessorNumber].ApFunction = 0; + CpuMpData->CpuData[ProcessorNumber].ApFunction = 0; CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument = 0; - ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; - CpuInfoInHob[ProcessorNumber].ApTopOfStack = CpuInfoInHob[CpuMpData->NewBspNumber].ApTopOfStack; + ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; + CpuInfoInHob[ProcessorNumber].ApTopOfStack = CpuInfoInHob[CpuMpData->NewBspNumber].ApTopOfStack; } else { - // - // Re-get the CPU APICID and Initial APICID - // - CpuInfoInHob[ProcessorNumber].ApicId = GetApicId (); - CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId (); + if ((CpuInfoInHob[ProcessorNumber].ApicId != GetApicId ()) || + (CpuInfoInHob[ProcessorNumber].InitialApicId != GetInitialApicId ())) + { + if (CurrentApicMode != GetApicMode ()) { + // + // If APIC mode change happened during AP function execution, + // we do not support APIC ID value changed. + // + ASSERT (FALSE); + CpuDeadLoop (); + } else { + // + // Re-get the CPU APICID and Initial APICID if they are changed + // + CpuInfoInHob[ProcessorNumber].ApicId = GetApicId (); + CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId (); + } + } } } + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateFinished); } } + if (CpuMpData->ApLoopMode == ApInHltLoop) { + // + // Save AP volatile registers + // + SaveVolatileRegisters (&CpuMpData->CpuData[ProcessorNumber].VolatileRegisters); + } + // // AP finished executing C code // - InterlockedIncrement ((UINT32 *) &CpuMpData->FinishedCount); + InterlockedIncrement ((UINT32 *)&CpuMpData->FinishedCount); + + if (CpuMpData->InitFlag == ApInitConfig) { + // + // Delay decrementing the APs executing count when SEV-ES is enabled + // to allow the APs to issue an AP_RESET_HOLD before the BSP possibly + // performs another INIT-SIPI-SIPI sequence. + // + if (!CpuMpData->SevEsIsEnabled) { + InterlockedDecrement ((UINT32 *)&CpuMpData->MpCpuExchangeInfo->NumApsExecuting); + } + } // // Place AP is specified loop mode // if (CpuMpData->ApLoopMode == ApInHltLoop) { - // - // Save AP volatile registers - // - SaveVolatileRegisters (&CpuMpData->CpuData[ProcessorNumber].VolatileRegisters); // // Place AP in HLT-loop // while (TRUE) { DisableInterrupts (); - CpuSleep (); + if (CpuMpData->SevEsIsEnabled) { + SevEsPlaceApHlt (CpuMpData); + } else { + CpuSleep (); + } + CpuPause (); } } + while (TRUE) { DisableInterrupts (); if (CpuMpData->ApLoopMode == ApInMwaitLoop) { // // Place AP in MWAIT-loop // - AsmMonitor ((UINTN) ApStartupSignalBuffer, 0, 0); + AsmMonitor ((UINTN)ApStartupSignalBuffer, 0, 0); if (*ApStartupSignalBuffer != WAKEUP_AP_SIGNAL) { // // Check AP start-up signal again. @@ -684,7 +829,7 @@ ApWakeupFunction ( **/ VOID WaitApWakeup ( - IN volatile UINT32 *ApStartupSignalBuffer + IN volatile UINT32 *ApStartupSignalBuffer ) { // @@ -692,10 +837,11 @@ WaitApWakeup ( // Otherwise, write StartupApSignal again till AP waken up. // while (InterlockedCompareExchange32 ( - (UINT32 *) ApStartupSignalBuffer, - WAKEUP_AP_SIGNAL, - WAKEUP_AP_SIGNAL - ) != 0) { + (UINT32 *)ApStartupSignalBuffer, + WAKEUP_AP_SIGNAL, + WAKEUP_AP_SIGNAL + ) != 0) + { CpuPause (); } } @@ -708,36 +854,98 @@ WaitApWakeup ( **/ VOID FillExchangeInfoData ( - IN CPU_MP_DATA *CpuMpData + IN CPU_MP_DATA *CpuMpData ) { - volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; + volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; + UINTN Size; + IA32_SEGMENT_DESCRIPTOR *Selector; + IA32_CR4 Cr4; - ExchangeInfo = CpuMpData->MpCpuExchangeInfo; - ExchangeInfo->Lock = 0; - ExchangeInfo->StackStart = CpuMpData->Buffer; - ExchangeInfo->StackSize = CpuMpData->CpuApStackSize; - ExchangeInfo->BufferStart = CpuMpData->WakeupBuffer; - ExchangeInfo->ModeOffset = CpuMpData->AddressMap.ModeEntryOffset; + ExchangeInfo = CpuMpData->MpCpuExchangeInfo; + ExchangeInfo->StackStart = CpuMpData->Buffer; + ExchangeInfo->StackSize = CpuMpData->CpuApStackSize; + ExchangeInfo->BufferStart = CpuMpData->WakeupBuffer; + ExchangeInfo->ModeOffset = CpuMpData->AddressMap.ModeEntryOffset; - ExchangeInfo->CodeSegment = AsmReadCs (); - ExchangeInfo->DataSegment = AsmReadDs (); + ExchangeInfo->CodeSegment = AsmReadCs (); + ExchangeInfo->DataSegment = AsmReadDs (); - ExchangeInfo->Cr3 = AsmReadCr3 (); + ExchangeInfo->Cr3 = AsmReadCr3 (); - ExchangeInfo->CFunction = (UINTN) ApWakeupFunction; + ExchangeInfo->CFunction = (UINTN)ApWakeupFunction; + ExchangeInfo->ApIndex = 0; ExchangeInfo->NumApsExecuting = 0; - ExchangeInfo->InitFlag = (UINTN) CpuMpData->InitFlag; - ExchangeInfo->CpuInfo = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + ExchangeInfo->InitFlag = (UINTN)CpuMpData->InitFlag; + ExchangeInfo->CpuInfo = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; ExchangeInfo->CpuMpData = CpuMpData; ExchangeInfo->EnableExecuteDisable = IsBspExecuteDisableEnabled (); + 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)); + + ExchangeInfo->SevEsIsEnabled = CpuMpData->SevEsIsEnabled; + ExchangeInfo->GhcbBase = (UINTN)CpuMpData->GhcbBase; + // // Get the BSP's data of GDT and IDT // - AsmReadGdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->GdtrProfile); - AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile); + 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.SwitchToRealSize - + 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; } /** @@ -750,11 +958,159 @@ FillExchangeInfoData ( **/ VOID TimedWaitForApFinish ( - IN CPU_MP_DATA *CpuMpData, - IN UINT32 FinishedApLimit, - IN UINT32 TimeLimit + IN CPU_MP_DATA *CpuMpData, + IN UINT32 FinishedApLimit, + IN UINT32 TimeLimit ); +/** + Get available system memory below 1MB by specified size. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +BackupAndPrepareWakeupBuffer ( + IN CPU_MP_DATA *CpuMpData + ) +{ + CopyMem ( + (VOID *)CpuMpData->BackupBuffer, + (VOID *)CpuMpData->WakeupBuffer, + CpuMpData->BackupBufferSize + ); + CopyMem ( + (VOID *)CpuMpData->WakeupBuffer, + (VOID *)CpuMpData->AddressMap.RendezvousFunnelAddress, + CpuMpData->AddressMap.RendezvousFunnelSize + + CpuMpData->AddressMap.SwitchToRealSize + ); +} + +/** + Restore wakeup buffer data. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +RestoreWakeupBuffer ( + IN CPU_MP_DATA *CpuMpData + ) +{ + CopyMem ( + (VOID *)CpuMpData->WakeupBuffer, + (VOID *)CpuMpData->BackupBuffer, + CpuMpData->BackupBufferSize + ); +} + +/** + Calculate the size of the reset vector. + + @param[in] AddressMap The pointer to Address Map structure. + + @return Total amount of memory required for the AP reset area +**/ +STATIC +UINTN +GetApResetVectorSize ( + IN MP_ASSEMBLY_ADDRESS_MAP *AddressMap + ) +{ + UINTN Size; + + Size = AddressMap->RendezvousFunnelSize + + AddressMap->SwitchToRealSize + + sizeof (MP_CPU_EXCHANGE_INFO); + + return Size; +} + +/** + Allocate reset vector buffer. + + @param[in, out] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +AllocateResetVector ( + IN OUT CPU_MP_DATA *CpuMpData + ) +{ + UINTN ApResetVectorSize; + UINTN ApResetStackSize; + + if (CpuMpData->WakeupBuffer == (UINTN)-1) { + ApResetVectorSize = GetApResetVectorSize (&CpuMpData->AddressMap); + + CpuMpData->WakeupBuffer = GetWakeupBuffer (ApResetVectorSize); + CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *)(UINTN) + (CpuMpData->WakeupBuffer + + CpuMpData->AddressMap.RendezvousFunnelSize + + CpuMpData->AddressMap.SwitchToRealSize); + CpuMpData->WakeupBufferHigh = GetModeTransitionBuffer ( + CpuMpData->AddressMap.RendezvousFunnelSize + + CpuMpData->AddressMap.SwitchToRealSize - + CpuMpData->AddressMap.ModeTransitionOffset + ); + // + // The AP reset stack is only used by SEV-ES guests. Do not allocate it + // if SEV-ES is not enabled. + // + if (ConfidentialComputingGuestHas (CCAttrAmdSevEs)) { + // + // Stack location is based on ProcessorNumber, so use the total number + // of processors for calculating the total stack area. + // + ApResetStackSize = (AP_RESET_STACK_SIZE * + PcdGet32 (PcdCpuMaxLogicalProcessorNumber)); + + // + // Invoke GetWakeupBuffer a second time to allocate the stack area + // below 1MB. The returned buffer will be page aligned and sized and + // below the previously allocated buffer. + // + CpuMpData->SevEsAPResetStackStart = GetWakeupBuffer (ApResetStackSize); + + // + // Check to be sure that the "allocate below" behavior hasn't changed. + // This will also catch a failed allocation, as "-1" is returned on + // failure. + // + if (CpuMpData->SevEsAPResetStackStart >= CpuMpData->WakeupBuffer) { + DEBUG (( + DEBUG_ERROR, + "SEV-ES AP reset stack is not below wakeup buffer\n" + )); + + ASSERT (FALSE); + CpuDeadLoop (); + } + } + } + + BackupAndPrepareWakeupBuffer (CpuMpData); +} + +/** + Free AP reset vector buffer. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +FreeResetVector ( + IN CPU_MP_DATA *CpuMpData + ) +{ + // + // If SEV-ES is enabled, the reset area is needed for AP parking and + // and AP startup in the OS, so the reset area is reserved. Do not + // perform the restore as this will overwrite memory which has data + // needed by SEV-ES. + // + if (!CpuMpData->SevEsIsEnabled) { + RestoreWakeupBuffer (CpuMpData); + } +} + /** This function will be called by BSP to wakeup AP. @@ -764,32 +1120,38 @@ TimedWaitForApFinish ( @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 ( - IN CPU_MP_DATA *CpuMpData, - IN BOOLEAN Broadcast, - IN UINTN ProcessorNumber, - IN EFI_AP_PROCEDURE Procedure, OPTIONAL - IN VOID *ProcedureArgument OPTIONAL + IN CPU_MP_DATA *CpuMpData, + IN BOOLEAN Broadcast, + IN UINTN ProcessorNumber, + IN EFI_AP_PROCEDURE Procedure OPTIONAL, + IN VOID *ProcedureArgument OPTIONAL, + IN BOOLEAN WakeUpDisabledAps ) { - volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; - UINTN Index; - CPU_AP_DATA *CpuData; - BOOLEAN ResetVectorRequired; - CPU_INFO_IN_HOB *CpuInfoInHob; + volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; + UINTN Index; + CPU_AP_DATA *CpuData; + BOOLEAN ResetVectorRequired; + CPU_INFO_IN_HOB *CpuInfoInHob; CpuMpData->FinishedCount = 0; - ResetVectorRequired = FALSE; + ResetVectorRequired = FALSE; - if (CpuMpData->ApLoopMode == ApInHltLoop || - CpuMpData->InitFlag != ApInitDone) { + if (CpuMpData->WakeUpByInitSipiSipi || + (CpuMpData->InitFlag != ApInitDone)) + { ResetVectorRequired = TRUE; AllocateResetVector (CpuMpData); + AllocateSevEsAPMemory (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 @@ -803,29 +1165,103 @@ WakeUpAP ( for (Index = 0; Index < CpuMpData->CpuCount; Index++) { if (Index != CpuMpData->BspNumber) { CpuData = &CpuMpData->CpuData[Index]; - CpuData->ApFunction = (UINTN) Procedure; - CpuData->ApFunctionArgument = (UINTN) ProcedureArgument; + // + // 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); if (CpuMpData->InitFlag != ApInitConfig) { - *(UINT32 *) CpuData->StartupApSignal = WAKEUP_AP_SIGNAL; + *(UINT32 *)CpuData->StartupApSignal = WAKEUP_AP_SIGNAL; } } } + if (ResetVectorRequired) { + // + // For SEV-ES, the initial AP boot address will be defined by + // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address + // from the original INIT-SIPI-SIPI. + // + if (CpuMpData->SevEsIsEnabled) { + SetSevEsJumpTable (ExchangeInfo->BufferStart); + } + // // Wakeup all APs // - SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart); + SendInitSipiSipiAllExcludingSelf ((UINT32)ExchangeInfo->BufferStart); } + if (CpuMpData->InitFlag == ApInitConfig) { - // - // Wait for all potential APs waken up in one specified period - // - 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 (); + } + } } else { // // Wait all APs waken up if this is not the 1st broadcast of SIPI @@ -838,22 +1274,33 @@ WakeUpAP ( } } } else { - CpuData = &CpuMpData->CpuData[ProcessorNumber]; - CpuData->ApFunction = (UINTN) Procedure; - CpuData->ApFunctionArgument = (UINTN) ProcedureArgument; + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + CpuData->ApFunction = (UINTN)Procedure; + CpuData->ApFunctionArgument = (UINTN)ProcedureArgument; SetApState (CpuData, CpuStateReady); // // Wakeup specified AP // ASSERT (CpuMpData->InitFlag != ApInitConfig); - *(UINT32 *) CpuData->StartupApSignal = WAKEUP_AP_SIGNAL; + *(UINT32 *)CpuData->StartupApSignal = WAKEUP_AP_SIGNAL; if (ResetVectorRequired) { - CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; + + // + // For SEV-ES, the initial AP boot address will be defined by + // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address + // from the original INIT-SIPI-SIPI. + // + if (CpuMpData->SevEsIsEnabled) { + SetSevEsJumpTable (ExchangeInfo->BufferStart); + } + SendInitSipiSipi ( CpuInfoInHob[ProcessorNumber].ApicId, - (UINT32) ExchangeInfo->BufferStart + (UINT32)ExchangeInfo->BufferStart ); } + // // Wait specified AP waken up // @@ -863,6 +1310,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); } /** @@ -886,6 +1340,9 @@ CalculateTimeout ( OUT UINT64 *CurrentTime ) { + UINT64 TimeoutInSeconds; + UINT64 TimestampCounterFreq; + // // Read the current value of the performance counter // @@ -901,16 +1358,36 @@ CalculateTimeout ( // // GetPerformanceCounterProperties () returns the timestamp counter's frequency - // in Hz. So multiply the return value with TimeoutInMicroseconds and then divide - // it by 1,000,000, to get the number of ticks for the timeout value. - // - return DivU64x32 ( - MultU64x64 ( - GetPerformanceCounterProperties (NULL, NULL), - TimeoutInMicroseconds - ), - 1000000 - ); + // in Hz. + // + TimestampCounterFreq = GetPerformanceCounterProperties (NULL, NULL); + + // + // Check the potential overflow before calculate the number of ticks for the timeout value. + // + if (DivU64x64Remainder (MAX_UINT64, TimeoutInMicroseconds, NULL) < TimestampCounterFreq) { + // + // Convert microseconds into seconds if direct multiplication overflows + // + TimeoutInSeconds = DivU64x32 (TimeoutInMicroseconds, 1000000); + // + // Assertion if the final tick count exceeds MAX_UINT64 + // + ASSERT (DivU64x64Remainder (MAX_UINT64, TimeoutInSeconds, NULL) >= TimestampCounterFreq); + return MultU64x64 (TimestampCounterFreq, TimeoutInSeconds); + } else { + // + // No overflow case, multiply the return value with TimeoutInMicroseconds and then divide + // it by 1,000,000, to get the number of ticks for the timeout value. + // + return DivU64x32 ( + MultU64x64 ( + TimestampCounterFreq, + TimeoutInMicroseconds + ), + 1000000 + ); + } } /** @@ -949,25 +1426,30 @@ CheckTimeout ( if (Timeout == 0) { return FALSE; } + GetPerformanceCounterProperties (&Start, &End); Cycle = End - Start; if (Cycle < 0) { Cycle = -Cycle; } + Cycle++; - CurrentTime = GetPerformanceCounter(); - Delta = (INT64) (CurrentTime - *PreviousTime); + CurrentTime = GetPerformanceCounter (); + Delta = (INT64)(CurrentTime - *PreviousTime); if (Start > End) { Delta = -Delta; } + if (Delta < 0) { Delta += Cycle; } - *TotalTime += Delta; + + *TotalTime += Delta; *PreviousTime = CurrentTime; if (*TotalTime > Timeout) { return TRUE; } + return FALSE; } @@ -981,9 +1463,9 @@ CheckTimeout ( **/ VOID TimedWaitForApFinish ( - IN CPU_MP_DATA *CpuMpData, - IN UINT32 FinishedApLimit, - IN UINT32 TimeLimit + IN CPU_MP_DATA *CpuMpData, + IN UINT32 FinishedApLimit, + IN UINT32 TimeLimit ) { // @@ -994,7 +1476,7 @@ TimedWaitForApFinish ( return; } - CpuMpData->TotalTime = 0; + CpuMpData->TotalTime = 0; CpuMpData->ExpectedTime = CalculateTimeout ( TimeLimit, &CpuMpData->CurrentTime @@ -1004,7 +1486,8 @@ TimedWaitForApFinish ( &CpuMpData->CurrentTime, &CpuMpData->TotalTime, CpuMpData->ExpectedTime - )) { + )) + { CpuPause (); } @@ -1033,18 +1516,19 @@ TimedWaitForApFinish ( **/ VOID ResetProcessorToIdleState ( - IN UINTN ProcessorNumber + IN UINTN ProcessorNumber ) { - CPU_MP_DATA *CpuMpData; + CPU_MP_DATA *CpuMpData; CpuMpData = GetCpuMpData (); CpuMpData->InitFlag = ApInitReconfig; - WakeUpAP (CpuMpData, FALSE, ProcessorNumber, NULL, NULL); + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, NULL, NULL, TRUE); while (CpuMpData->FinishedCount < 1) { CpuPause (); } + CpuMpData->InitFlag = ApInitDone; SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); @@ -1063,11 +1547,11 @@ ResetProcessorToIdleState ( **/ EFI_STATUS GetNextWaitingProcessorNumber ( - OUT UINTN *NextProcessorNumber + OUT UINTN *NextProcessorNumber ) { - UINTN ProcessorNumber; - CPU_MP_DATA *CpuMpData; + UINTN ProcessorNumber; + CPU_MP_DATA *CpuMpData; CpuMpData = GetCpuMpData (); @@ -1094,27 +1578,28 @@ GetNextWaitingProcessorNumber ( **/ EFI_STATUS CheckThisAP ( - IN UINTN ProcessorNumber + IN UINTN ProcessorNumber ) { - CPU_MP_DATA *CpuMpData; - CPU_AP_DATA *CpuData; + CPU_MP_DATA *CpuMpData; + CPU_AP_DATA *CpuData; CpuMpData = GetCpuMpData (); 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. // - if (GetApState(CpuData) == CpuStateFinished) { + if (GetApState (CpuData) == CpuStateFinished) { if (CpuData->Finished != NULL) { *(CpuData->Finished) = TRUE; } + SetApState (CpuData, CpuStateIdle); return EFI_SUCCESS; } else { @@ -1125,6 +1610,7 @@ CheckThisAP ( if (CpuData->Finished != NULL) { *(CpuData->Finished) = FALSE; } + // // Reset failed AP to idle state // @@ -1133,6 +1619,7 @@ CheckThisAP ( return EFI_TIMEOUT; } } + return EFI_NOT_READY; } @@ -1151,12 +1638,12 @@ CheckAllAPs ( VOID ) { - UINTN ProcessorNumber; - UINTN NextProcessorNumber; - UINTN ListIndex; - EFI_STATUS Status; - CPU_MP_DATA *CpuMpData; - CPU_AP_DATA *CpuData; + UINTN ProcessorNumber; + UINTN NextProcessorNumber; + UINTN ListIndex; + EFI_STATUS Status; + CPU_MP_DATA *CpuMpData; + CPU_AP_DATA *CpuData; CpuMpData = GetCpuMpData (); @@ -1172,14 +1659,14 @@ 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 ++; + if (GetApState (CpuData) == CpuStateFinished) { + CpuMpData->RunningCount--; CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE; - SetApState(CpuData, CpuStateIdle); + SetApState (CpuData, CpuStateIdle); // // If in Single Thread mode, then search for the next waiting AP for execution. @@ -1191,11 +1678,12 @@ CheckAllAPs ( WakeUpAP ( CpuMpData, FALSE, - (UINT32) NextProcessorNumber, + (UINT32)NextProcessorNumber, CpuMpData->Procedure, - CpuMpData->ProcArguments + CpuMpData->ProcArguments, + TRUE ); - } + } } } } @@ -1203,7 +1691,7 @@ CheckAllAPs ( // // If all APs finish, return EFI_SUCCESS. // - if (CpuMpData->RunningCount == CpuMpData->StartCount) { + if (CpuMpData->RunningCount == 0) { return EFI_SUCCESS; } @@ -1211,18 +1699,21 @@ CheckAllAPs ( // If timeout expires, report timeout. // if (CheckTimeout ( - &CpuMpData->CurrentTime, - &CpuMpData->TotalTime, - CpuMpData->ExpectedTime) - ) { + &CpuMpData->CurrentTime, + &CpuMpData->TotalTime, + CpuMpData->ExpectedTime + ) + ) + { // // If FailedCpuList is not NULL, record all failed APs in it. // 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; for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { @@ -1240,11 +1731,14 @@ CheckAllAPs ( } } } + if (CpuMpData->FailedCpuList != NULL) { (*CpuMpData->FailedCpuList)[ListIndex] = END_OF_CPU_LIST; } + return EFI_TIMEOUT; } + return EFI_NOT_READY; } @@ -1272,6 +1766,7 @@ MpInitLibInitialize ( UINT32 MaxLogicalProcessorNumber; UINT32 ApStackSize; MP_ASSEMBLY_ADDRESS_MAP AddressMap; + CPU_VOLATILE_REGISTERS VolatileRegisters; UINTN BufferSize; UINT32 MonitorFilterSize; VOID *MpBuffer; @@ -1282,54 +1777,103 @@ MpInitLibInitialize ( UINTN Index; UINTN ApResetVectorSize; UINTN BackupBufferAddr; + UINTN ApIdtBase; OldCpuMpData = GetCpuMpDataFromGuidedHob (); if (OldCpuMpData == NULL) { - MaxLogicalProcessorNumber = PcdGet32(PcdCpuMaxLogicalProcessorNumber); + MaxLogicalProcessorNumber = PcdGet32 (PcdCpuMaxLogicalProcessorNumber); } else { MaxLogicalProcessorNumber = OldCpuMpData->CpuCount; } + ASSERT (MaxLogicalProcessorNumber != 0); AsmGetAddressMap (&AddressMap); - ApResetVectorSize = AddressMap.RendezvousFunnelSize + sizeof (MP_CPU_EXCHANGE_INFO); - ApStackSize = PcdGet32(PcdCpuApStackSize); - ApLoopMode = GetApLoopMode (&MonitorFilterSize); + ApResetVectorSize = GetApResetVectorSize (&AddressMap); + 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; - - MonitorBuffer = (UINT8 *) (Buffer + ApStackSize * MaxLogicalProcessorNumber); - BackupBufferAddr = (UINTN) MonitorBuffer + MonitorFilterSize * MaxLogicalProcessorNumber; - CpuMpData = (CPU_MP_DATA *) (BackupBufferAddr + ApResetVectorSize); + 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; + ApIdtBase = ALIGN_VALUE (BackupBufferAddr + ApResetVectorSize, 8); + CpuMpData = (CPU_MP_DATA *)(ApIdtBase + VolatileRegisters.Idtr.Limit + 1); CpuMpData->Buffer = Buffer; CpuMpData->CpuApStackSize = ApStackSize; CpuMpData->BackupBuffer = BackupBufferAddr; CpuMpData->BackupBufferSize = ApResetVectorSize; - CpuMpData->SaveRestoreFlag = FALSE; - CpuMpData->WakeupBuffer = (UINTN) -1; + CpuMpData->WakeupBuffer = (UINTN)-1; CpuMpData->CpuCount = 1; CpuMpData->BspNumber = 0; CpuMpData->WaitEvent = NULL; CpuMpData->SwitchBspFlag = FALSE; - CpuMpData->CpuData = (CPU_AP_DATA *) (CpuMpData + 1); - CpuMpData->CpuInfoInHob = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber); - InitializeSpinLock(&CpuMpData->MpLock); + CpuMpData->CpuData = (CPU_AP_DATA *)(CpuMpData + 1); + CpuMpData->CpuInfoInHob = (UINT64)(UINTN)(CpuMpData->CpuData + MaxLogicalProcessorNumber); + InitializeSpinLock (&CpuMpData->MpLock); + CpuMpData->SevEsIsEnabled = ConfidentialComputingGuestHas (CCAttrAmdSevEs); + CpuMpData->SevEsAPBuffer = (UINTN)-1; + CpuMpData->GhcbBase = PcdGet64 (PcdGhcbBase); + + // + // Make sure no memory usage outside of the allocated buffer. + // + ASSERT ( + (CpuMpData->CpuInfoInHob + sizeof (CPU_INFO_IN_HOB) * MaxLogicalProcessorNumber) == + Buffer + BufferSize + ); + // - // Save BSP's Control registers to APs + // 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 // - SaveVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters); + CopyMem ((VOID *)ApIdtBase, (VOID *)VolatileRegisters.Idtr.Base, VolatileRegisters.Idtr.Limit + 1); + VolatileRegisters.Idtr.Base = ApIdtBase; + // + // 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 // @@ -1339,6 +1883,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 // @@ -1346,14 +1893,11 @@ MpInitLibInitialize ( CpuMpData->CpuData[Index].StartupApSignal = (UINT32 *)(MonitorBuffer + MonitorFilterSize * Index); } + // - // Load Microcode on BSP - // - MicrocodeDetect (CpuMpData); - // - // Store BSP's MTRR setting + // Enable the local APIC for Virtual Wire Mode. // - MtrrGetAllMtrrs (&CpuMpData->MtrrTable); + ProgramVirtualWireMode (); if (OldCpuMpData == NULL) { if (MaxLogicalProcessorNumber > 1) { @@ -1367,42 +1911,100 @@ MpInitLibInitialize ( // APs have been wakeup before, just get the CPU Information // from HOB // - CpuMpData->CpuCount = OldCpuMpData->CpuCount; - CpuMpData->BspNumber = OldCpuMpData->BspNumber; - CpuMpData->InitFlag = ApInitReconfig; - CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob; - CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + OldCpuMpData->NewCpuMpData = CpuMpData; + CpuMpData->CpuCount = OldCpuMpData->CpuCount; + CpuMpData->BspNumber = OldCpuMpData->BspNumber; + 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) { - CpuMpData->X2ApicEnable = TRUE; - } - CpuMpData->CpuData[Index].CpuHealthy = (CpuInfoInHob[Index].Health == 0)? TRUE:FALSE; + InitializeSpinLock (&CpuMpData->CpuData[Index].ApLock); + 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) - ); } - if (MaxLogicalProcessorNumber > 1) { + } + + 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) { + if (OldCpuMpData != NULL) { // - // Wakeup APs to do some AP initialize sync + // Only needs to use this flag for DXE phase to update the wake up + // buffer. Wakeup buffer allocated in PEI phase is no longer valid + // in DXE. // - WakeUpAP (CpuMpData, TRUE, 0, ApInitializeSync, CpuMpData); + CpuMpData->InitFlag = ApInitReconfig; + } + + WakeUpAP (CpuMpData, TRUE, 0, ApInitializeSync, CpuMpData, TRUE); + // + // Wait for all APs finished initialization + // + while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) { + CpuPause (); + } + + if (OldCpuMpData != NULL) { + CpuMpData->InitFlag = ApInitDone; + } + + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + SetApState (&CpuMpData->CpuData[Index], CpuStateIdle); + } + } + + // + // Dump the microcode revision for each core. + // + DEBUG_CODE_BEGIN (); + UINT32 ThreadId; + UINT32 ExpectedMicrocodeRevision; + + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + GetProcessorLocationByApicId (CpuInfoInHob[Index].InitialApicId, NULL, NULL, &ThreadId); + if (ThreadId == 0) { // - // Wait for all APs finished initialization + // MicrocodeDetect() loads microcode in first thread of each core, so, + // CpuMpData->CpuData[Index].MicrocodeEntryAddr is initialized only for first thread of each core. // - while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) { - CpuPause (); - } - CpuMpData->InitFlag = ApInitDone; - for (Index = 0; Index < CpuMpData->CpuCount; Index++) { - SetApState (&CpuMpData->CpuData[Index], CpuStateIdle); + ExpectedMicrocodeRevision = 0; + if (CpuMpData->CpuData[Index].MicrocodeEntryAddr != 0) { + ExpectedMicrocodeRevision = ((CPU_MICROCODE_HEADER *)(UINTN)CpuMpData->CpuData[Index].MicrocodeEntryAddr)->UpdateRevision; } + + DEBUG (( + DEBUG_INFO, + "CPU[%04d]: Microcode revision = %08x, expected = %08x\n", + Index, + CpuMpData->CpuData[Index].MicrocodeRevision, + ExpectedMicrocodeRevision + )); } } + DEBUG_CODE_END (); // // Initialize global data for MP support // @@ -1436,12 +2038,19 @@ MpInitLibGetProcessorInfo ( OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL ) { - CPU_MP_DATA *CpuMpData; - UINTN CallerNumber; - CPU_INFO_IN_HOB *CpuInfoInHob; + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + CPU_INFO_IN_HOB *CpuInfoInHob; + UINTN OriginalProcessorNumber; - CpuMpData = GetCpuMpData (); - CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + CpuMpData = GetCpuMpData (); + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; + + // + // Lower 24 bits contains the actual processor number. + // + OriginalProcessorNumber = ProcessorNumber; + ProcessorNumber &= BIT24 - 1; // // Check whether caller processor is BSP @@ -1459,14 +2068,16 @@ MpInitLibGetProcessorInfo ( return EFI_NOT_FOUND; } - ProcessorInfoBuffer->ProcessorId = (UINT64) CpuInfoInHob[ProcessorNumber].ApicId; + ProcessorInfoBuffer->ProcessorId = (UINT64)CpuInfoInHob[ProcessorNumber].ApicId; ProcessorInfoBuffer->StatusFlag = 0; if (ProcessorNumber == CpuMpData->BspNumber) { ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT; } + if (CpuMpData->CpuData[ProcessorNumber].CpuHealthy) { ProcessorInfoBuffer->StatusFlag |= PROCESSOR_HEALTH_STATUS_BIT; } + if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateDisabled) { ProcessorInfoBuffer->StatusFlag &= ~PROCESSOR_ENABLED_BIT; } else { @@ -1483,6 +2094,18 @@ MpInitLibGetProcessorInfo ( &ProcessorInfoBuffer->Location.Thread ); + if ((OriginalProcessorNumber & CPU_V2_EXTENDED_TOPOLOGY) != 0) { + GetProcessorLocation2ByApicId ( + CpuInfoInHob[ProcessorNumber].ApicId, + &ProcessorInfoBuffer->ExtendedInformation.Location2.Package, + &ProcessorInfoBuffer->ExtendedInformation.Location2.Die, + &ProcessorInfoBuffer->ExtendedInformation.Location2.Tile, + &ProcessorInfoBuffer->ExtendedInformation.Location2.Module, + &ProcessorInfoBuffer->ExtendedInformation.Location2.Core, + &ProcessorInfoBuffer->ExtendedInformation.Location2.Thread + ); + } + if (HealthData != NULL) { HealthData->Uint32 = CpuInfoInHob[ProcessorNumber].Health; } @@ -1498,13 +2121,13 @@ 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 SwitchBSPWorker ( - IN UINTN ProcessorNumber, - IN BOOLEAN EnableOldBSP + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP ) { CPU_MP_DATA *CpuMpData; @@ -1540,7 +2163,7 @@ SwitchBSPWorker ( // MpInitLibWhoAmI (&CallerNumber); if (CallerNumber != CpuMpData->BspNumber) { - return EFI_SUCCESS; + return EFI_DEVICE_ERROR; } if (ProcessorNumber >= CpuMpData->CpuCount) { @@ -1577,23 +2200,24 @@ SwitchBSPWorker ( // // Clear the BSP bit of MSR_IA32_APIC_BASE // - ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); ApicBaseMsr.Bits.BSP = 0; AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); // // Need to wakeUp AP (future BSP). // - WakeUpAP (CpuMpData, FALSE, ProcessorNumber, FutureBSPProc, CpuMpData); + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, FutureBSPProc, CpuMpData, TRUE); AsmExchangeRole (&CpuMpData->BSPInfo, &CpuMpData->APInfo); // // Set the BSP bit of MSR_IA32_APIC_BASE on new BSP // - ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + 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 @@ -1611,10 +2235,11 @@ SwitchBSPWorker ( } else { SetApState (&CpuMpData->CpuData[CallerNumber], CpuStateIdle); } + // // Save new BSP number // - CpuMpData->BspNumber = (UINT32) ProcessorNumber; + CpuMpData->BspNumber = (UINT32)ProcessorNumber; // // Restore interrupt state. @@ -1644,13 +2269,13 @@ SwitchBSPWorker ( **/ EFI_STATUS EnableDisableApWorker ( - IN UINTN ProcessorNumber, - IN BOOLEAN EnableAP, - IN UINT32 *HealthFlag OPTIONAL + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL ) { - CPU_MP_DATA *CpuMpData; - UINTN CallerNumber; + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; CpuMpData = GetCpuMpData (); @@ -1673,12 +2298,12 @@ EnableDisableApWorker ( if (!EnableAP) { SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateDisabled); } else { - SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); + ResetProcessorToIdleState (ProcessorNumber); } if (HealthFlag != NULL) { CpuMpData->CpuData[ProcessorNumber].CpuHealthy = - (BOOLEAN) ((*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT) != 0); + (BOOLEAN)((*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT) != 0); } return EFI_SUCCESS; @@ -1703,10 +2328,10 @@ EnableDisableApWorker ( EFI_STATUS EFIAPI MpInitLibWhoAmI ( - OUT UINTN *ProcessorNumber + OUT UINTN *ProcessorNumber ) { - CPU_MP_DATA *CpuMpData; + CPU_MP_DATA *CpuMpData; if (ProcessorNumber == NULL) { return EFI_INVALID_PARAMETER; @@ -1740,15 +2365,15 @@ MpInitLibWhoAmI ( EFI_STATUS EFIAPI MpInitLibGetNumberOfProcessors ( - OUT UINTN *NumberOfProcessors, OPTIONAL - OUT UINTN *NumberOfEnabledProcessors OPTIONAL + OUT UINTN *NumberOfProcessors OPTIONAL, + OUT UINTN *NumberOfEnabledProcessors OPTIONAL ) { - CPU_MP_DATA *CpuMpData; - UINTN CallerNumber; - UINTN ProcessorNumber; - UINTN EnabledProcessorNumber; - UINTN Index; + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + UINTN ProcessorNumber; + UINTN EnabledProcessorNumber; + UINTN Index; CpuMpData = GetCpuMpData (); @@ -1768,13 +2393,14 @@ MpInitLibGetNumberOfProcessors ( EnabledProcessorNumber = 0; for (Index = 0; Index < ProcessorNumber; Index++) { if (GetApState (&CpuMpData->CpuData[Index]) != CpuStateDisabled) { - EnabledProcessorNumber ++; + EnabledProcessorNumber++; } } if (NumberOfProcessors != NULL) { *NumberOfProcessors = ProcessorNumber; } + if (NumberOfEnabledProcessors != NULL) { *NumberOfEnabledProcessors = EnabledProcessorNumber; } @@ -1782,7 +2408,6 @@ MpInitLibGetNumberOfProcessors ( return EFI_SUCCESS; } - /** Worker function to execute a caller provided function on all enabled APs. @@ -1794,6 +2419,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 @@ -1815,23 +2441,24 @@ MpInitLibGetNumberOfProcessors ( **/ EFI_STATUS -StartupAllAPsWorker ( - IN EFI_AP_PROCEDURE Procedure, - IN BOOLEAN SingleThread, - IN EFI_EVENT WaitEvent OPTIONAL, - IN UINTN TimeoutInMicroseconds, - IN VOID *ProcedureArgument OPTIONAL, - OUT UINTN **FailedCpuList OPTIONAL +StartupAllCPUsWorker ( + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN BOOLEAN ExcludeBsp, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT UINTN **FailedCpuList OPTIONAL ) { - EFI_STATUS Status; - CPU_MP_DATA *CpuMpData; - UINTN ProcessorCount; - UINTN ProcessorNumber; - UINTN CallerNumber; - CPU_AP_DATA *CpuData; - BOOLEAN HasEnabledAp; - CPU_STATE ApState; + EFI_STATUS Status; + CPU_MP_DATA *CpuMpData; + UINTN ProcessorCount; + UINTN ProcessorNumber; + UINTN CallerNumber; + CPU_AP_DATA *CpuData; + BOOLEAN HasEnabledAp; + CPU_STATE ApState; CpuMpData = GetCpuMpData (); @@ -1839,7 +2466,7 @@ StartupAllAPsWorker ( *FailedCpuList = NULL; } - if (CpuMpData->CpuCount == 1) { + if ((CpuMpData->CpuCount == 1) && ExcludeBsp) { return EFI_NOT_STARTED; } @@ -1882,16 +2509,16 @@ 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 = &CpuMpData->CpuData[ProcessorNumber]; CpuData->Waiting = FALSE; if (ProcessorNumber != CpuMpData->BspNumber) { if (CpuData->State == CpuStateIdle) { @@ -1899,7 +2526,7 @@ StartupAllAPsWorker ( // Mark this processor as responsible for current calling. // CpuData->Waiting = TRUE; - CpuMpData->StartCount++; + CpuMpData->RunningCount++; } } } @@ -1908,29 +2535,36 @@ StartupAllAPsWorker ( CpuMpData->ProcArguments = ProcedureArgument; CpuMpData->SingleThread = SingleThread; CpuMpData->FinishedCount = 0; - CpuMpData->RunningCount = 0; CpuMpData->FailedCpuList = FailedCpuList; CpuMpData->ExpectedTime = CalculateTimeout ( TimeoutInMicroseconds, &CpuMpData->CurrentTime ); - CpuMpData->TotalTime = 0; - CpuMpData->WaitEvent = WaitEvent; + CpuMpData->TotalTime = 0; + 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 { @@ -1966,18 +2600,18 @@ StartupAllAPsWorker ( **/ EFI_STATUS StartupThisAPWorker ( - IN EFI_AP_PROCEDURE Procedure, - IN UINTN ProcessorNumber, - IN EFI_EVENT WaitEvent OPTIONAL, - IN UINTN TimeoutInMicroseconds, - IN VOID *ProcedureArgument OPTIONAL, - OUT BOOLEAN *Finished OPTIONAL + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT BOOLEAN *Finished OPTIONAL ) { - EFI_STATUS Status; - CPU_MP_DATA *CpuMpData; - CPU_AP_DATA *CpuData; - UINTN CallerNumber; + EFI_STATUS Status; + CPU_MP_DATA *CpuMpData; + CPU_AP_DATA *CpuData; + UINTN CallerNumber; CpuMpData = GetCpuMpData (); @@ -2031,13 +2665,13 @@ StartupThisAPWorker ( // BSP saves data for CheckAPsStatus(), and returns EFI_SUCCESS. // CheckAPsStatus() will check completion and timeout periodically. // - CpuData = &CpuMpData->CpuData[ProcessorNumber]; + CpuData = &CpuMpData->CpuData[ProcessorNumber]; CpuData->WaitEvent = WaitEvent; CpuData->Finished = Finished; 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. @@ -2063,54 +2697,128 @@ GetCpuMpDataFromGuidedHob ( VOID ) { - EFI_HOB_GUID_TYPE *GuidHob; - VOID *DataInHob; - CPU_MP_DATA *CpuMpData; + EFI_HOB_GUID_TYPE *GuidHob; + VOID *DataInHob; + CPU_MP_DATA *CpuMpData; CpuMpData = NULL; - GuidHob = GetFirstGuidHob (&mCpuInitMpLibHobGuid); + GuidHob = GetFirstGuidHob (&mCpuInitMpLibHobGuid); if (GuidHob != NULL) { DataInHob = GET_GUID_HOB_DATA (GuidHob); - CpuMpData = (CPU_MP_DATA *) (*(UINTN *) DataInHob); + CpuMpData = (CPU_MP_DATA *)(*(UINTN *)DataInHob); } + return CpuMpData; } /** - Get available system memory below 1MB by specified size. + 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. - @param[in] CpuMpData The pointer to CPU MP Data structure. **/ -VOID -BackupAndPrepareWakeupBuffer( - IN CPU_MP_DATA *CpuMpData +EFI_STATUS +EFIAPI +MpInitLibStartupAllCPUs ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL ) { - CopyMem ( - (VOID *) CpuMpData->BackupBuffer, - (VOID *) CpuMpData->WakeupBuffer, - CpuMpData->BackupBufferSize - ); - CopyMem ( - (VOID *) CpuMpData->WakeupBuffer, - (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress, - CpuMpData->AddressMap.RendezvousFunnelSize - ); + return StartupAllCPUsWorker ( + Procedure, + FALSE, + FALSE, + NULL, + TimeoutInMicroseconds, + ProcedureArgument, + NULL + ); } /** - Restore wakeup buffer data. + The function check if the specified Attr is set. + + @param[in] CurrentAttr The current attribute. + @param[in] Attr The attribute to check. + + @retval TRUE The specified Attr is set. + @retval FALSE The specified Attr is not set. - @param[in] CpuMpData The pointer to CPU MP Data structure. **/ -VOID -RestoreWakeupBuffer( - IN CPU_MP_DATA *CpuMpData +STATIC +BOOLEAN +AmdMemEncryptionAttrCheck ( + IN UINT64 CurrentAttr, + IN CONFIDENTIAL_COMPUTING_GUEST_ATTR Attr ) { - CopyMem ( - (VOID *) CpuMpData->WakeupBuffer, - (VOID *) CpuMpData->BackupBuffer, - CpuMpData->BackupBufferSize - ); + switch (Attr) { + case CCAttrAmdSev: + // + // SEV is automatically enabled if SEV-ES or SEV-SNP is active. + // + return CurrentAttr >= CCAttrAmdSev; + case CCAttrAmdSevEs: + // + // SEV-ES is automatically enabled if SEV-SNP is active. + // + return CurrentAttr >= CCAttrAmdSevEs; + case CCAttrAmdSevSnp: + return CurrentAttr == CCAttrAmdSevSnp; + default: + return FALSE; + } +} + +/** + Check if the specified confidential computing attribute is active. + + @param[in] Attr The attribute to check. + + @retval TRUE The specified Attr is active. + @retval FALSE The specified Attr is not active. + +**/ +BOOLEAN +EFIAPI +ConfidentialComputingGuestHas ( + IN CONFIDENTIAL_COMPUTING_GUEST_ATTR Attr + ) +{ + UINT64 CurrentAttr; + + // + // Get the current CC attribute. + // + CurrentAttr = PcdGet64 (PcdConfidentialComputingGuestAttr); + + // + // If attr is for the AMD group then call AMD specific checks. + // + if (((RShiftU64 (CurrentAttr, 8)) & 0xff) == 1) { + return AmdMemEncryptionAttrCheck (CurrentAttr, Attr); + } + + return (CurrentAttr == Attr); }