X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=UefiCpuPkg%2FLibrary%2FMpInitLib%2FMpLib.c;h=ff09a0e9e7e843f2332a0d78fb7b24a138414ee5;hp=c3fe72138fb564f5e42488d64e72fc427ef3c005;hb=2a5997f899bc0bb2851d3ba7168b78b36444e6f0;hpb=42c37b3b3322a597b62e14852bcb3030c86ea3c4 diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c index c3fe72138f..ff09a0e9e7 100644 --- a/UefiCpuPkg/Library/MpInitLib/MpLib.c +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c @@ -1,7 +1,7 @@ /** @file CPU MP Initialize Library common functions. - Copyright (c) 2016, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, 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 @@ -18,8 +18,11 @@ 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 +36,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); + 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; + } } } } @@ -57,132 +67,6 @@ IsBspExecuteDisableEnabled ( return Enabled; } -/** - Get CPU Package/Core/Thread location information. - - @param[in] InitialApicId CPU APIC ID - @param[out] Location Pointer to CPU location information -**/ -VOID -ExtractProcessorLocation ( - IN UINT32 InitialApicId, - OUT EFI_CPU_PHYSICAL_LOCATION *Location - ) -{ - BOOLEAN TopologyLeafSupported; - UINTN ThreadBits; - UINTN CoreBits; - CPUID_VERSION_INFO_EBX VersionInfoEbx; - CPUID_VERSION_INFO_EDX VersionInfoEdx; - CPUID_CACHE_PARAMS_EAX CacheParamsEax; - CPUID_EXTENDED_TOPOLOGY_EAX ExtendedTopologyEax; - CPUID_EXTENDED_TOPOLOGY_EBX ExtendedTopologyEbx; - CPUID_EXTENDED_TOPOLOGY_ECX ExtendedTopologyEcx; - UINT32 MaxCpuIdIndex; - UINT32 SubIndex; - UINTN LevelType; - UINT32 MaxLogicProcessorsPerPackage; - UINT32 MaxCoresPerPackage; - - // - // Check if the processor is capable of supporting more than one logical processor. - // - AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32); - if (VersionInfoEdx.Bits.HTT == 0) { - Location->Thread = 0; - Location->Core = 0; - Location->Package = 0; - return; - } - - ThreadBits = 0; - CoreBits = 0; - - // - // Assume three-level mapping of APIC ID: Package:Core:SMT. - // - - TopologyLeafSupported = FALSE; - // - // Get the max index of basic CPUID - // - AsmCpuid (CPUID_SIGNATURE, &MaxCpuIdIndex, NULL, NULL, NULL); - - // - // If the extended topology enumeration leaf is available, it - // is the preferred mechanism for enumerating topology. - // - if (MaxCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) { - AsmCpuidEx ( - CPUID_EXTENDED_TOPOLOGY, - 0, - &ExtendedTopologyEax.Uint32, - &ExtendedTopologyEbx.Uint32, - &ExtendedTopologyEcx.Uint32, - NULL - ); - // - // If CPUID.(EAX=0BH, ECX=0H):EBX returns zero and maximum input value for - // basic CPUID information is greater than 0BH, then CPUID.0BH leaf is not - // supported on that processor. - // - if (ExtendedTopologyEbx.Uint32 != 0) { - TopologyLeafSupported = TRUE; - - // - // Sub-leaf index 0 (ECX= 0 as input) provides enumeration parameters to extract - // the SMT sub-field of x2APIC ID. - // - LevelType = ExtendedTopologyEcx.Bits.LevelType; - ASSERT (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT); - ThreadBits = ExtendedTopologyEax.Bits.ApicIdShift; - - // - // Software must not assume any "level type" encoding - // value to be related to any sub-leaf index, except sub-leaf 0. - // - SubIndex = 1; - do { - AsmCpuidEx ( - CPUID_EXTENDED_TOPOLOGY, - SubIndex, - &ExtendedTopologyEax.Uint32, - NULL, - &ExtendedTopologyEcx.Uint32, - NULL - ); - LevelType = ExtendedTopologyEcx.Bits.LevelType; - if (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE) { - CoreBits = ExtendedTopologyEax.Bits.ApicIdShift - ThreadBits; - break; - } - SubIndex++; - } while (LevelType != CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID); - } - } - - if (!TopologyLeafSupported) { - AsmCpuid (CPUID_VERSION_INFO, NULL, &VersionInfoEbx.Uint32, NULL, NULL); - MaxLogicProcessorsPerPackage = VersionInfoEbx.Bits.MaximumAddressableIdsForLogicalProcessors; - if (MaxCpuIdIndex >= CPUID_CACHE_PARAMS) { - AsmCpuidEx (CPUID_CACHE_PARAMS, 0, &CacheParamsEax.Uint32, NULL, NULL, NULL); - MaxCoresPerPackage = CacheParamsEax.Bits.MaximumAddressableIdsForLogicalProcessors + 1; - } else { - // - // Must be a single-core processor. - // - MaxCoresPerPackage = 1; - } - - ThreadBits = (UINTN) (HighBitSet32 (MaxLogicProcessorsPerPackage / MaxCoresPerPackage - 1) + 1); - CoreBits = (UINTN) (HighBitSet32 (MaxCoresPerPackage - 1) + 1); - } - - Location->Thread = InitialApicId & ((1 << ThreadBits) - 1); - Location->Core = (InitialApicId >> ThreadBits) & ((1 << CoreBits) - 1); - Location->Package = (InitialApicId >> (ThreadBits + CoreBits)); -} - /** Worker function for SwitchBSP(). @@ -235,6 +119,53 @@ SetApState ( ReleaseSpinLock (&CpuData->ApLock); } +/** + Save BSP's local APIC timer setting. + + @param[in] CpuMpData Pointer to CPU MP Data +**/ +VOID +SaveLocalApicTimerSetting ( + IN CPU_MP_DATA *CpuMpData + ) +{ + // + // Record the current local APIC timer setting of BSP + // + GetApicTimerState ( + &CpuMpData->DivideValue, + &CpuMpData->PeriodicMode, + &CpuMpData->Vector + ); + CpuMpData->CurrentTimerCount = GetApicTimerCurrentCount (); + CpuMpData->TimerInterruptState = GetApicTimerInterruptState (); +} + +/** + Sync local APIC timer setting from BSP to AP. + + @param[in] CpuMpData Pointer to CPU MP Data +**/ +VOID +SyncLocalApicTimerSetting ( + IN CPU_MP_DATA *CpuMpData + ) +{ + // + // Sync local APIC timer setting from BSP to AP + // + InitializeApicTimer ( + CpuMpData->DivideValue, + CpuMpData->CurrentTimerCount, + CpuMpData->PeriodicMode, + CpuMpData->Vector + ); + // + // Disable AP's local APIC timer interrupt + // + DisableApicTimerInterrupt (); +} + /** Save the volatile registers required to be restored following INIT IPI. @@ -264,6 +195,10 @@ SaveVolatileRegisters ( VolatileRegisters->Dr6 = AsmReadDr6 (); VolatileRegisters->Dr7 = AsmReadDr7 (); } + + AsmReadGdtr (&VolatileRegisters->Gdtr); + AsmReadIdtr (&VolatileRegisters->Idtr); + VolatileRegisters->Tr = AsmReadTr (); } /** @@ -280,6 +215,7 @@ RestoreVolatileRegisters ( ) { CPUID_VERSION_INFO_EDX VersionInfoEdx; + IA32_TSS_DESCRIPTOR *Tss; AsmWriteCr0 (VolatileRegisters->Cr0); AsmWriteCr3 (VolatileRegisters->Cr3); @@ -300,6 +236,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); + } + } } /** @@ -379,33 +327,42 @@ SortApicId ( UINTN Index2; UINTN Index3; UINT32 ApicId; - CPU_AP_DATA CpuData; + 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; // // Sort key is the hardware default APIC ID // - ApicId = CpuMpData->CpuData[Index1].ApicId; + ApicId = CpuInfoInHob[Index1].ApicId; for (Index2 = Index1 + 1; Index2 <= ApCount; Index2++) { - if (ApicId > CpuMpData->CpuData[Index2].ApicId) { + if (ApicId > CpuInfoInHob[Index2].ApicId) { Index3 = Index2; - ApicId = CpuMpData->CpuData[Index2].ApicId; + ApicId = CpuInfoInHob[Index2].ApicId; } } if (Index3 != Index1) { - CopyMem (&CpuData, &CpuMpData->CpuData[Index3], sizeof (CPU_AP_DATA)); + CopyMem (&CpuInfo, &CpuInfoInHob[Index3], sizeof (CPU_INFO_IN_HOB)); CopyMem ( - &CpuMpData->CpuData[Index3], - &CpuMpData->CpuData[Index1], - sizeof (CPU_AP_DATA) + &CpuInfoInHob[Index3], + &CpuInfoInHob[Index1], + sizeof (CPU_INFO_IN_HOB) ); - CopyMem (&CpuMpData->CpuData[Index1], &CpuData, sizeof (CPU_AP_DATA)); + 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; } } @@ -414,18 +371,11 @@ SortApicId ( // ApicId = GetInitialApicId (); for (Index1 = 0; Index1 < CpuMpData->CpuCount; Index1++) { - if (CpuMpData->CpuData[Index1].ApicId == ApicId) { + if (CpuInfoInHob[Index1].ApicId == ApicId) { CpuMpData->BspNumber = (UINT32) Index1; break; } } - - CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; - for (Index1 = 0; Index1 < CpuMpData->CpuCount; Index1++) { - CpuInfoInHob[Index1].InitialApicId = CpuMpData->CpuData[Index1].InitialApicId; - CpuInfoInHob[Index1].ApicId = CpuMpData->CpuData[Index1].ApicId; - CpuInfoInHob[Index1].Health = CpuMpData->CpuData[Index1].Health; - } } } @@ -458,20 +408,20 @@ ApInitializeSync ( CpuMpData = (CPU_MP_DATA *) Buffer; // - // Sync BSP's MTRR table to AP + // Load microcode on AP // - MtrrSetAllMtrrs (&CpuMpData->MtrrTable); + MicrocodeDetect (CpuMpData, FALSE); // - // Load microcode on AP + // Sync BSP's MTRR table to AP // - MicrocodeDetect (CpuMpData); + MtrrSetAllMtrrs (&CpuMpData->MtrrTable); } /** Find the current Processor number by APIC ID. - @param[in] CpuMpData Pointer to PEI CPU MP Data - @param[in] ProcessorNumber Return the pocessor number found + @param[in] CpuMpData Pointer to PEI CPU MP Data + @param[out] ProcessorNumber Return the pocessor number found @retval EFI_SUCCESS ProcessorNumber is found and returned. @retval EFI_NOT_FOUND ProcessorNumber is not found. @@ -484,14 +434,20 @@ 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 (CpuMpData->CpuData[Index].ApicId == GetApicId ()) { + if (CpuInfoInHob[Index].ApicId == CurrentApicId) { *ProcessorNumber = Index; return EFI_SUCCESS; } } + return EFI_NOT_FOUND; } @@ -507,6 +463,8 @@ CollectProcessorCount ( IN CPU_MP_DATA *CpuMpData ) { + UINTN Index; + // // Send 1st broadcast IPI to APs to wakeup APs // @@ -522,6 +480,12 @@ CollectProcessorCount ( CpuPause (); } + if (CpuMpData->CpuCount > 255) { + // + // If there are more than 255 processor found, force to enable X2APIC + // + CpuMpData->X2ApicEnable = TRUE; + } if (CpuMpData->X2ApicEnable) { DEBUG ((DEBUG_INFO, "Force x2APIC mode!\n")); // @@ -538,6 +502,12 @@ CollectProcessorCount ( // 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 ())); // @@ -550,27 +520,34 @@ CollectProcessorCount ( return CpuMpData->CpuCount; } -/* +/** Initialize CPU AP Data when AP is wakeup at the first time. @param[in, out] CpuMpData Pointer to PEI CPU MP Data @param[in] ProcessorNumber The handle number of processor @param[in] BistData Processor BIST data + @param[in] ApTopOfStack Top of AP stack **/ VOID InitializeApData ( IN OUT CPU_MP_DATA *CpuMpData, IN UINTN ProcessorNumber, - IN UINT32 BistData + IN UINT32 BistData, + IN UINT64 ApTopOfStack ) { + CPU_INFO_IN_HOB *CpuInfoInHob; + + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId (); + CpuInfoInHob[ProcessorNumber].ApicId = GetApicId (); + CpuInfoInHob[ProcessorNumber].Health = BistData; + CpuInfoInHob[ProcessorNumber].ApTopOfStack = ApTopOfStack; + CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE; - CpuMpData->CpuData[ProcessorNumber].Health = BistData; CpuMpData->CpuData[ProcessorNumber].CpuHealthy = (BistData == 0) ? TRUE : FALSE; - CpuMpData->CpuData[ProcessorNumber].ApicId = GetApicId (); - CpuMpData->CpuData[ProcessorNumber].InitialApicId = GetInitialApicId (); - if (CpuMpData->CpuData[ProcessorNumber].InitialApicId >= 0xFF) { + if (CpuInfoInHob[ProcessorNumber].InitialApicId >= 0xFF) { // // Set x2APIC mode if there are any logical processor reporting // an Initial APIC ID of 255 or greater. @@ -588,13 +565,13 @@ 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 UINTN ApIndex ) { CPU_MP_DATA *CpuMpData; @@ -603,34 +580,50 @@ ApWakeupFunction ( 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 // CpuMpData = ExchangeInfo->CpuMpData; - ProgramVirtualWireMode (); + // + // AP's local APIC settings will be lost after received INIT IPI + // 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 // - BistData = *(UINT32 *) (CpuMpData->Buffer + ProcessorNumber * CpuMpData->CpuApStackSize - 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); + InitializeApData (CpuMpData, ProcessorNumber, BistData, ApTopOfStack); ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; } else { // @@ -651,6 +644,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) { @@ -659,9 +659,14 @@ ApWakeupFunction ( 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; if (CpuMpData->SwitchBspFlag) { // // Re-get the processor number due to BSP/AP maybe exchange in AP function @@ -669,15 +674,29 @@ ApWakeupFunction ( GetProcessorNumber (CpuMpData, &ProcessorNumber); CpuMpData->CpuData[ProcessorNumber].ApFunction = 0; CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument = 0; + ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; + CpuInfoInHob[ProcessorNumber].ApTopOfStack = CpuInfoInHob[CpuMpData->NewBspNumber].ApTopOfStack; } else { - // - // Re-get the CPU APICID and Initial APICID - // - CpuMpData->CpuData[ProcessorNumber].ApicId = GetApicId (); - CpuMpData->CpuData[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); + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); } } @@ -685,6 +704,7 @@ ApWakeupFunction ( // AP finished executing C code // InterlockedIncrement ((UINT32 *) &CpuMpData->FinishedCount); + InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting); // // Place AP is specified loop mode @@ -773,6 +793,8 @@ FillExchangeInfoData ( ) { volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; + UINTN Size; + IA32_SEGMENT_DESCRIPTOR *Selector; ExchangeInfo = CpuMpData->MpCpuExchangeInfo; ExchangeInfo->Lock = 0; @@ -787,16 +809,155 @@ FillExchangeInfoData ( ExchangeInfo->Cr3 = AsmReadCr3 (); 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->CpuMpData = CpuMpData; ExchangeInfo->EnableExecuteDisable = IsBspExecuteDisableEnabled (); + ExchangeInfo->InitializeFloatingPointUnitsAddress = (UINTN)InitializeFloatingPointUnits; + // // 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; +} + +/** + Helper function that waits until the finished AP count reaches the specified + limit, or the specified timeout elapses (whichever comes first). + + @param[in] CpuMpData Pointer to CPU MP Data. + @param[in] FinishedApLimit The number of finished APs to wait for. + @param[in] TimeLimit The number of microseconds to wait for. +**/ +VOID +TimedWaitForApFinish ( + 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 + ); +} + +/** + 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 + ); +} + +/** + 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; + + if (CpuMpData->WakeupBuffer == (UINTN) -1) { + ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize + + sizeof (MP_CPU_EXCHANGE_INFO); + + 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); +} + +/** + Free AP reset vector buffer. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +FreeResetVector ( + IN CPU_MP_DATA *CpuMpData + ) +{ + RestoreWakeupBuffer (CpuMpData); } /** @@ -822,16 +983,20 @@ WakeUpAP ( UINTN Index; CPU_AP_DATA *CpuData; BOOLEAN ResetVectorRequired; + CPU_INFO_IN_HOB *CpuInfoInHob; CpuMpData->FinishedCount = 0; ResetVectorRequired = FALSE; - if (CpuMpData->ApLoopMode == ApInHltLoop || + if (CpuMpData->WakeUpByInitSipiSipi || CpuMpData->InitFlag != ApInitDone) { ResetVectorRequired = TRUE; AllocateResetVector (CpuMpData); FillExchangeInfoData (CpuMpData); - } else if (CpuMpData->ApLoopMode == ApInMwaitLoop) { + SaveLocalApicTimerSetting (CpuMpData); + } + + if (CpuMpData->ApLoopMode == ApInMwaitLoop) { // // Get AP target C-state each time when waking up AP, // for it maybe updated by platform again @@ -861,9 +1026,24 @@ WakeUpAP ( } if (CpuMpData->InitFlag == ApInitConfig) { // - // Wait for all potential APs waken up in one specified period + // Here support two methods to collect AP count through adjust + // PcdCpuApInitTimeOutInMicroSeconds values. // - MicroSecondDelay (PcdGet32(PcdCpuApInitTimeOutInMicroSeconds)); + // 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) + ); + + while (CpuMpData->MpCpuExchangeInfo->NumApsExecuting != 0) { + CpuPause(); + } } else { // // Wait all APs waken up if this is not the 1st broadcast of SIPI @@ -886,8 +1066,9 @@ WakeUpAP ( ASSERT (CpuMpData->InitFlag != ApInitConfig); *(UINT32 *) CpuData->StartupApSignal = WAKEUP_AP_SIGNAL; if (ResetVectorRequired) { + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; SendInitSipiSipi ( - CpuData->ApicId, + CpuInfoInHob[ProcessorNumber].ApicId, (UINT32) ExchangeInfo->BufferStart ); } @@ -900,6 +1081,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); } /** @@ -923,6 +1111,9 @@ CalculateTimeout ( OUT UINT64 *CurrentTime ) { + UINT64 TimeoutInSeconds; + UINT64 TimestampCounterFreq; + // // Read the current value of the performance counter // @@ -938,16 +1129,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 + ); + } } /** @@ -1008,6 +1219,58 @@ CheckTimeout ( return FALSE; } +/** + Helper function that waits until the finished AP count reaches the specified + limit, or the specified timeout elapses (whichever comes first). + + @param[in] CpuMpData Pointer to CPU MP Data. + @param[in] FinishedApLimit The number of finished APs to wait for. + @param[in] TimeLimit The number of microseconds to wait for. +**/ +VOID +TimedWaitForApFinish ( + IN CPU_MP_DATA *CpuMpData, + IN UINT32 FinishedApLimit, + IN UINT32 TimeLimit + ) +{ + // + // CalculateTimeout() and CheckTimeout() consider a TimeLimit of 0 + // "infinity", so check for (TimeLimit == 0) explicitly. + // + if (TimeLimit == 0) { + return; + } + + CpuMpData->TotalTime = 0; + CpuMpData->ExpectedTime = CalculateTimeout ( + TimeLimit, + &CpuMpData->CurrentTime + ); + while (CpuMpData->FinishedCount < FinishedApLimit && + !CheckTimeout ( + &CpuMpData->CurrentTime, + &CpuMpData->TotalTime, + CpuMpData->ExpectedTime + )) { + CpuPause (); + } + + if (CpuMpData->FinishedCount >= FinishedApLimit) { + DEBUG (( + DEBUG_VERBOSE, + "%a: reached FinishedApLimit=%u in %Lu microseconds\n", + __FUNCTION__, + FinishedApLimit, + DivU64x64Remainder ( + MultU64x32 (CpuMpData->TotalTime, 1000000), + GetPerformanceCounterProperties (NULL, NULL), + NULL + ) + )); + } +} + /** Reset an AP to Idle state. @@ -1025,7 +1288,12 @@ ResetProcessorToIdleState ( CpuMpData = GetCpuMpData (); + CpuMpData->InitFlag = ApInitReconfig; WakeUpAP (CpuMpData, FALSE, ProcessorNumber, NULL, NULL); + while (CpuMpData->FinishedCount < 1) { + CpuPause (); + } + CpuMpData->InitFlag = ApInitDone; SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); } @@ -1084,18 +1352,17 @@ 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. // - if (GetApState(CpuData) == CpuStateFinished) { + if (GetApState(CpuData) == CpuStateIdle) { if (CpuData->Finished != NULL) { *(CpuData->Finished) = TRUE; } - SetApState (CpuData, CpuStateIdle); return EFI_SUCCESS; } else { // @@ -1152,14 +1419,13 @@ 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) { + if (GetApState(CpuData) == CpuStateIdle) { CpuMpData->RunningCount ++; CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE; - SetApState(CpuData, CpuStateIdle); // // If in Single Thread mode, then search for the next waiting AP for execution. @@ -1252,6 +1518,7 @@ MpInitLibInitialize ( UINT32 MaxLogicalProcessorNumber; UINT32 ApStackSize; MP_ASSEMBLY_ADDRESS_MAP AddressMap; + CPU_VOLATILE_REGISTERS VolatileRegisters; UINTN BufferSize; UINT32 MonitorFilterSize; VOID *MpBuffer; @@ -1262,6 +1529,8 @@ MpInitLibInitialize ( UINTN Index; UINTN ApResetVectorSize; UINTN BackupBufferAddr; + UINTN ApIdtBase; + VOID *MicrocodePatchInRam; OldCpuMpData = GetCpuMpDataFromGuidedHob (); if (OldCpuMpData == NULL) { @@ -1269,30 +1538,59 @@ MpInitLibInitialize ( } else { MaxLogicalProcessorNumber = OldCpuMpData->CpuCount; } + ASSERT (MaxLogicalProcessorNumber != 0); AsmGetAddressMap (&AddressMap); ApResetVectorSize = AddressMap.RendezvousFunnelSize + sizeof (MP_CPU_EXCHANGE_INFO); 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; CpuMpData->BackupBufferSize = ApResetVectorSize; - CpuMpData->SaveRestoreFlag = FALSE; CpuMpData->WakeupBuffer = (UINTN) -1; CpuMpData->CpuCount = 1; CpuMpData->BspNumber = 0; @@ -1300,15 +1598,57 @@ MpInitLibInitialize ( CpuMpData->SwitchBspFlag = FALSE; CpuMpData->CpuData = (CPU_AP_DATA *) (CpuMpData + 1); CpuMpData->CpuInfoInHob = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber); + CpuMpData->MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize); + // + // If platform has more than one CPU, relocate microcode to memory to reduce + // loading microcode time. + // + MicrocodePatchInRam = NULL; + if (MaxLogicalProcessorNumber > 1) { + MicrocodePatchInRam = AllocatePages ( + EFI_SIZE_TO_PAGES ( + (UINTN)CpuMpData->MicrocodePatchRegionSize + ) + ); + } + if (MicrocodePatchInRam == NULL) { + // + // there is only one processor, or no microcode patch is available, or + // memory allocation failed + // + CpuMpData->MicrocodePatchAddress = PcdGet64 (PcdCpuMicrocodePatchAddress); + } else { + // + // there are multiple processors, and a microcode patch is available, and + // memory allocation succeeded + // + CopyMem ( + MicrocodePatchInRam, + (VOID *)(UINTN)PcdGet64 (PcdCpuMicrocodePatchAddress), + (UINTN)CpuMpData->MicrocodePatchRegionSize + ); + CpuMpData->MicrocodePatchAddress = (UINTN)MicrocodePatchInRam; + } + InitializeSpinLock(&CpuMpData->MpLock); + + // + // 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; + CopyMem (&CpuMpData->CpuData[0].VolatileRegisters, &VolatileRegisters, sizeof (VolatileRegisters)); // // Set BSP basic information // - InitializeApData (CpuMpData, 0, 0); + InitializeApData (CpuMpData, 0, 0, CpuMpData->Buffer + ApStackSize); // // Save assembly code information // @@ -1318,6 +1658,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 // @@ -1328,17 +1671,23 @@ MpInitLibInitialize ( // // Load Microcode on BSP // - MicrocodeDetect (CpuMpData); + MicrocodeDetect (CpuMpData, TRUE); // // Store BSP's MTRR setting // MtrrGetAllMtrrs (&CpuMpData->MtrrTable); + // + // Enable the local APIC for Virtual Wire Mode. + // + ProgramVirtualWireMode (); if (OldCpuMpData == NULL) { - // - // Wakeup all APs and calculate the processor count in system - // - CollectProcessorCount (CpuMpData); + if (MaxLogicalProcessorNumber > 1) { + // + // Wakeup all APs and calculate the processor count in system + // + CollectProcessorCount (CpuMpData); + } } else { // // APs have been wakeup before, just get the CPU Information @@ -1347,36 +1696,32 @@ MpInitLibInitialize ( CpuMpData->CpuCount = OldCpuMpData->CpuCount; CpuMpData->BspNumber = OldCpuMpData->BspNumber; CpuMpData->InitFlag = ApInitReconfig; - CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) OldCpuMpData->CpuInfoInHob; + CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob; + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; for (Index = 0; Index < CpuMpData->CpuCount; Index++) { InitializeSpinLock(&CpuMpData->CpuData[Index].ApLock); - CpuMpData->CpuData[Index].ApicId = CpuInfoInHob[Index].ApicId; - CpuMpData->CpuData[Index].InitialApicId = CpuInfoInHob[Index].InitialApicId; - if (CpuMpData->CpuData[Index].InitialApicId >= 255) { + if (CpuInfoInHob[Index].InitialApicId >= 255 || Index > 254) { CpuMpData->X2ApicEnable = TRUE; } - CpuMpData->CpuData[Index].Health = CpuInfoInHob[Index].Health; - CpuMpData->CpuData[Index].CpuHealthy = (CpuMpData->CpuData[Index].Health == 0)? TRUE:FALSE; + 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) - ); - } - // - // 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 (); + CopyMem (&CpuMpData->CpuData[Index].VolatileRegisters, &VolatileRegisters, sizeof (CPU_VOLATILE_REGISTERS)); } - CpuMpData->InitFlag = ApInitDone; - for (Index = 0; Index < CpuMpData->CpuCount; Index++) { - SetApState (&CpuMpData->CpuData[Index], CpuStateIdle); + 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); + } } } @@ -1415,8 +1760,10 @@ MpInitLibGetProcessorInfo ( { CPU_MP_DATA *CpuMpData; UINTN CallerNumber; + CPU_INFO_IN_HOB *CpuInfoInHob; CpuMpData = GetCpuMpData (); + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; // // Check whether caller processor is BSP @@ -1434,7 +1781,7 @@ MpInitLibGetProcessorInfo ( return EFI_NOT_FOUND; } - ProcessorInfoBuffer->ProcessorId = (UINT64) CpuMpData->CpuData[ProcessorNumber].ApicId; + ProcessorInfoBuffer->ProcessorId = (UINT64) CpuInfoInHob[ProcessorNumber].ApicId; ProcessorInfoBuffer->StatusFlag = 0; if (ProcessorNumber == CpuMpData->BspNumber) { ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT; @@ -1451,10 +1798,15 @@ MpInitLibGetProcessorInfo ( // // Get processor location information // - ExtractProcessorLocation (CpuMpData->CpuData[ProcessorNumber].ApicId, &ProcessorInfoBuffer->Location); + GetProcessorLocationByApicId ( + CpuInfoInHob[ProcessorNumber].ApicId, + &ProcessorInfoBuffer->Location.Package, + &ProcessorInfoBuffer->Location.Core, + &ProcessorInfoBuffer->Location.Thread + ); if (HealthData != NULL) { - HealthData->Uint32 = CpuMpData->CpuData[ProcessorNumber].Health; + HealthData->Uint32 = CpuInfoInHob[ProcessorNumber].Health; } return EFI_SUCCESS; @@ -1468,7 +1820,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 @@ -1481,6 +1833,27 @@ SwitchBSPWorker ( UINTN CallerNumber; CPU_STATE State; MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr; + BOOLEAN OldInterruptState; + BOOLEAN OldTimerInterruptState; + + // + // Save and Disable Local APIC timer interrupt + // + OldTimerInterruptState = GetApicTimerInterruptState (); + DisableApicTimerInterrupt (); + // + // Before send both BSP and AP to a procedure to exchange their roles, + // interrupt must be disabled. This is because during the exchange role + // process, 2 CPU may use 1 stack. If interrupt happens, the stack will + // be corrupted, since interrupt return address will be pushed to stack + // by hardware. + // + OldInterruptState = SaveAndDisableInterrupts (); + + // + // Mask LINT0 & LINT1 for the old BSP + // + DisableLvtInterrupts (); CpuMpData = GetCpuMpData (); @@ -1489,7 +1862,7 @@ SwitchBSPWorker ( // MpInitLibWhoAmI (&CallerNumber); if (CallerNumber != CpuMpData->BspNumber) { - return EFI_SUCCESS; + return EFI_DEVICE_ERROR; } if (ProcessorNumber >= CpuMpData->CpuCount) { @@ -1521,6 +1894,7 @@ SwitchBSPWorker ( CpuMpData->BSPInfo.State = CPU_SWITCH_STATE_IDLE; CpuMpData->APInfo.State = CPU_SWITCH_STATE_IDLE; CpuMpData->SwitchBspFlag = TRUE; + CpuMpData->NewBspNumber = ProcessorNumber; // // Clear the BSP bit of MSR_IA32_APIC_BASE @@ -1542,11 +1916,12 @@ 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 // - while (GetApState (&CpuMpData->CpuData[CallerNumber]) != CpuStateFinished) { + while (GetApState (&CpuMpData->CpuData[CallerNumber]) != CpuStateIdle) { CpuPause (); } @@ -1556,12 +1931,23 @@ SwitchBSPWorker ( // if (!EnableOldBSP) { SetApState (&CpuMpData->CpuData[CallerNumber], CpuStateDisabled); + } else { + SetApState (&CpuMpData->CpuData[CallerNumber], CpuStateIdle); } // // Save new BSP number // CpuMpData->BspNumber = (UINT32) ProcessorNumber; + // + // Restore interrupt state. + // + SetInterruptState (OldInterruptState); + + if (OldTimerInterruptState) { + EnableApicTimerInterrupt (); + } + return EFI_SUCCESS; } @@ -1610,7 +1996,7 @@ EnableDisableApWorker ( if (!EnableAP) { SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateDisabled); } else { - SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); + ResetProcessorToIdleState (ProcessorNumber); } if (HealthFlag != NULL) { @@ -1733,7 +2119,7 @@ MpInitLibGetNumberOfProcessors ( simultaneously. @param[in] WaitEvent The event created by the caller with CreateEvent() service. - @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for APs to return from Procedure, either for blocking or non-blocking mode. @param[in] ProcedureArgument The parameter passed into Procedure for @@ -1887,7 +2273,7 @@ StartupAllAPsWorker ( @param[in] ProcessorNumber The handle number of the AP. @param[in] WaitEvent The event created by the caller with CreateEvent() service. - @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for APs to return from Procedure, either for blocking or non-blocking mode. @param[in] ProcedureArgument The parameter passed into Procedure for @@ -2013,41 +2399,3 @@ GetCpuMpDataFromGuidedHob ( return CpuMpData; } -/** - 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 - ); -} - -/** - 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 - ); -}