X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=UefiCpuPkg%2FLibrary%2FMpInitLib%2FMpLib.c;h=ff09a0e9e7e843f2332a0d78fb7b24a138414ee5;hp=48f930b701473de77cabd58d4f929fc4c6091ea1;hb=2a5997f899bc0bb2851d3ba7168b78b36444e6f0;hpb=0594ec417c89ed00cbea6a0b0458334727c4dc28 diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c index 48f930b701..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 - 2017, 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 @@ -195,6 +195,10 @@ SaveVolatileRegisters ( VolatileRegisters->Dr6 = AsmReadDr6 (); VolatileRegisters->Dr7 = AsmReadDr7 (); } + + AsmReadGdtr (&VolatileRegisters->Gdtr); + AsmReadIdtr (&VolatileRegisters->Idtr); + VolatileRegisters->Tr = AsmReadTr (); } /** @@ -211,6 +215,7 @@ RestoreVolatileRegisters ( ) { CPUID_VERSION_INFO_EDX VersionInfoEdx; + IA32_TSS_DESCRIPTOR *Tss; AsmWriteCr0 (VolatileRegisters->Cr0); AsmWriteCr3 (VolatileRegisters->Cr3); @@ -231,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); + } + } } /** @@ -313,6 +330,7 @@ SortApicId ( CPU_INFO_IN_HOB CpuInfo; UINT32 ApCount; CPU_INFO_IN_HOB *CpuInfoInHob; + volatile UINT32 *StartupApSignal; ApCount = CpuMpData->CpuCount - 1; CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; @@ -337,6 +355,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; } } @@ -384,7 +410,7 @@ ApInitializeSync ( // // Load microcode on AP // - MicrocodeDetect (CpuMpData); + MicrocodeDetect (CpuMpData, FALSE); // // Sync BSP's MTRR table to AP // @@ -409,16 +435,19 @@ GetProcessorNumber ( UINTN TotalProcessorNumber; UINTN Index; CPU_INFO_IN_HOB *CpuInfoInHob; + UINT32 CurrentApicId; CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; TotalProcessorNumber = CpuMpData->CpuCount; + CurrentApicId = GetApicId (); for (Index = 0; Index < TotalProcessorNumber; Index ++) { - if (CpuInfoInHob[Index].ApicId == GetApicId ()) { + if (CpuInfoInHob[Index].ApicId == CurrentApicId) { *ProcessorNumber = Index; return EFI_SUCCESS; } } + return EFI_NOT_FOUND; } @@ -536,7 +565,7 @@ InitializeApData ( This function will be called from AP reset code if BSP uses WakeUpAP. @param[in] ExchangeInfo Pointer to the MP exchange info buffer - @param[in] NumApsExecuting Number of current executing AP + @param[in] ApIndex Number of current executing AP **/ VOID EFIAPI @@ -565,6 +594,10 @@ ApWakeupFunction ( // We need to re-initialize them at here // ProgramVirtualWireMode (); + // + // Mask the LINT0 and LINT1 so that AP doesn't enter the system timer interrupt handler. + // + DisableLvtInterrupts (); SyncLocalApicTimerSetting (CpuMpData); CurrentApicMode = GetApicMode (); @@ -585,7 +618,9 @@ ApWakeupFunction ( // 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); @@ -609,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) { @@ -618,7 +660,7 @@ ApWakeupFunction ( SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateBusy); // // Enable source debugging on AP function - // + // EnableDebugAgent (); // // Invoke AP function here @@ -654,7 +696,7 @@ ApWakeupFunction ( } } } - SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateFinished); + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); } } @@ -751,6 +793,8 @@ FillExchangeInfoData ( ) { volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; + UINTN Size; + IA32_SEGMENT_DESCRIPTOR *Selector; ExchangeInfo = CpuMpData->MpCpuExchangeInfo; ExchangeInfo->Lock = 0; @@ -780,6 +824,46 @@ FillExchangeInfoData ( // AsmReadGdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->GdtrProfile); AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile); + + // + // Find a 32-bit code segment + // + Selector = (IA32_SEGMENT_DESCRIPTOR *)ExchangeInfo->GdtrProfile.Base; + Size = ExchangeInfo->GdtrProfile.Limit + 1; + while (Size > 0) { + if (Selector->Bits.L == 0 && Selector->Bits.Type >= 8) { + ExchangeInfo->ModeTransitionSegment = + (UINT16)((UINTN)Selector - ExchangeInfo->GdtrProfile.Base); + break; + } + Selector += 1; + Size -= sizeof (IA32_SEGMENT_DESCRIPTOR); + } + + // + // Copy all 32-bit code and 64-bit code into memory with type of + // EfiBootServicesCode to avoid page fault if NX memory protection is enabled. + // + if (CpuMpData->WakeupBufferHigh != 0) { + Size = CpuMpData->AddressMap.RendezvousFunnelSize - + CpuMpData->AddressMap.ModeTransitionOffset; + CopyMem ( + (VOID *)CpuMpData->WakeupBufferHigh, + CpuMpData->AddressMap.RendezvousFunnelAddress + + CpuMpData->AddressMap.ModeTransitionOffset, + Size + ); + + ExchangeInfo->ModeTransitionMemory = (UINT32)CpuMpData->WakeupBufferHigh; + } else { + ExchangeInfo->ModeTransitionMemory = (UINT32) + (ExchangeInfo->BufferStart + CpuMpData->AddressMap.ModeTransitionOffset); + } + + ExchangeInfo->ModeHighMemory = ExchangeInfo->ModeTransitionMemory + + (UINT32)ExchangeInfo->ModeOffset - + (UINT32)CpuMpData->AddressMap.ModeTransitionOffset; + ExchangeInfo->ModeHighSegment = (UINT16)ExchangeInfo->CodeSegment; } /** @@ -855,6 +939,10 @@ AllocateResetVector ( CpuMpData->WakeupBuffer = GetWakeupBuffer (ApResetVectorSize); CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) (CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize); + CpuMpData->WakeupBufferHigh = GetModeTransitionBuffer ( + CpuMpData->AddressMap.RendezvousFunnelSize - + CpuMpData->AddressMap.ModeTransitionOffset + ); } BackupAndPrepareWakeupBuffer (CpuMpData); } @@ -900,13 +988,15 @@ WakeUpAP ( CpuMpData->FinishedCount = 0; ResetVectorRequired = FALSE; - if (CpuMpData->ApLoopMode == ApInHltLoop || + if (CpuMpData->WakeUpByInitSipiSipi || CpuMpData->InitFlag != ApInitDone) { ResetVectorRequired = TRUE; AllocateResetVector (CpuMpData); FillExchangeInfoData (CpuMpData); SaveLocalApicTimerSetting (CpuMpData); - } else if (CpuMpData->ApLoopMode == ApInMwaitLoop) { + } + + if (CpuMpData->ApLoopMode == ApInMwaitLoop) { // // Get AP target C-state each time when waking up AP, // for it maybe updated by platform again @@ -936,15 +1026,20 @@ WakeUpAP ( } if (CpuMpData->InitFlag == ApInitConfig) { // - // Wait for one potential AP waken up in one specified period + // Here support two methods to collect AP count through adjust + // PcdCpuApInitTimeOutInMicroSeconds values. // - if (CpuMpData->CpuCount == 0) { - TimedWaitForApFinish ( - CpuMpData, - PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1, - 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(); @@ -986,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); } /** @@ -1027,7 +1129,7 @@ CalculateTimeout ( // // GetPerformanceCounterProperties () returns the timestamp counter's frequency - // in Hz. + // in Hz. // TimestampCounterFreq = GetPerformanceCounterProperties (NULL, NULL); @@ -1250,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 { // @@ -1318,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. @@ -1418,6 +1518,7 @@ MpInitLibInitialize ( UINT32 MaxLogicalProcessorNumber; UINT32 ApStackSize; MP_ASSEMBLY_ADDRESS_MAP AddressMap; + CPU_VOLATILE_REGISTERS VolatileRegisters; UINTN BufferSize; UINT32 MonitorFilterSize; VOID *MpBuffer; @@ -1428,6 +1529,8 @@ MpInitLibInitialize ( UINTN Index; UINTN ApResetVectorSize; UINTN BackupBufferAddr; + UINTN ApIdtBase; + VOID *MicrocodePatchInRam; OldCpuMpData = GetCpuMpDataFromGuidedHob (); if (OldCpuMpData == NULL) { @@ -1442,19 +1545,48 @@ MpInitLibInitialize ( ApStackSize = PcdGet32(PcdCpuApStackSize); ApLoopMode = GetApLoopMode (&MonitorFilterSize); + // + // Save BSP's Control registers for APs + // + SaveVolatileRegisters (&VolatileRegisters); + BufferSize = ApStackSize * MaxLogicalProcessorNumber; BufferSize += MonitorFilterSize * MaxLogicalProcessorNumber; - BufferSize += sizeof (CPU_MP_DATA); BufferSize += ApResetVectorSize; + BufferSize = ALIGN_VALUE (BufferSize, 8); + BufferSize += VolatileRegisters.Idtr.Limit + 1; + BufferSize += sizeof (CPU_MP_DATA); BufferSize += (sizeof (CPU_AP_DATA) + sizeof (CPU_INFO_IN_HOB))* MaxLogicalProcessorNumber; MpBuffer = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize)); ASSERT (MpBuffer != NULL); ZeroMem (MpBuffer, BufferSize); Buffer = (UINTN) MpBuffer; + // + // The layout of the Buffer is as below: + // + // +--------------------+ <-- Buffer + // AP Stacks (N) + // +--------------------+ <-- MonitorBuffer + // AP Monitor Filters (N) + // +--------------------+ <-- BackupBufferAddr (CpuMpData->BackupBuffer) + // Backup Buffer + // +--------------------+ + // Padding + // +--------------------+ <-- ApIdtBase (8-byte boundary) + // AP IDT All APs share one separate IDT. So AP can get address of CPU_MP_DATA from IDT Base. + // +--------------------+ <-- CpuMpData + // CPU_MP_DATA + // +--------------------+ <-- CpuMpData->CpuData + // CPU_AP_DATA (N) + // +--------------------+ <-- CpuMpData->CpuInfoInHob + // CPU_INFO_IN_HOB (N) + // +--------------------+ + // MonitorBuffer = (UINT8 *) (Buffer + ApStackSize * MaxLogicalProcessorNumber); BackupBufferAddr = (UINTN) MonitorBuffer + MonitorFilterSize * MaxLogicalProcessorNumber; - CpuMpData = (CPU_MP_DATA *) (BackupBufferAddr + ApResetVectorSize); + ApIdtBase = ALIGN_VALUE (BackupBufferAddr + ApResetVectorSize, 8); + CpuMpData = (CPU_MP_DATA *) (ApIdtBase + VolatileRegisters.Idtr.Limit + 1); CpuMpData->Buffer = Buffer; CpuMpData->CpuApStackSize = ApStackSize; CpuMpData->BackupBuffer = BackupBufferAddr; @@ -1466,17 +1598,57 @@ MpInitLibInitialize ( CpuMpData->SwitchBspFlag = FALSE; CpuMpData->CpuData = (CPU_AP_DATA *) (CpuMpData + 1); CpuMpData->CpuInfoInHob = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber); - CpuMpData->MicrocodePatchAddress = PcdGet64 (PcdCpuMicrocodePatchAddress); CpuMpData->MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize); + // + // 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); + // - // Save BSP's Control registers to APs + // Make sure no memory usage outside of the allocated buffer. // - SaveVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters); + ASSERT ((CpuMpData->CpuInfoInHob + sizeof (CPU_INFO_IN_HOB) * MaxLogicalProcessorNumber) == + Buffer + BufferSize); + + // + // Duplicate BSP's IDT to APs. + // All APs share one separate IDT. So AP can get the address of CpuMpData by using IDTR.BASE + IDTR.LIMIT + 1 + // + CopyMem ((VOID *)ApIdtBase, (VOID *)VolatileRegisters.Idtr.Base, VolatileRegisters.Idtr.Limit + 1); + VolatileRegisters.Idtr.Base = ApIdtBase; + 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 // @@ -1486,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 // @@ -1496,7 +1671,7 @@ MpInitLibInitialize ( // // Load Microcode on BSP // - MicrocodeDetect (CpuMpData); + MicrocodeDetect (CpuMpData, TRUE); // // Store BSP's MTRR setting // @@ -1530,11 +1705,7 @@ MpInitLibInitialize ( } CpuMpData->CpuData[Index].CpuHealthy = (CpuInfoInHob[Index].Health == 0)? TRUE:FALSE; CpuMpData->CpuData[Index].ApFunction = 0; - CopyMem ( - &CpuMpData->CpuData[Index].VolatileRegisters, - &CpuMpData->CpuData[0].VolatileRegisters, - sizeof (CPU_VOLATILE_REGISTERS) - ); + CopyMem (&CpuMpData->CpuData[Index].VolatileRegisters, &VolatileRegisters, sizeof (CPU_VOLATILE_REGISTERS)); } if (MaxLogicalProcessorNumber > 1) { // @@ -1649,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 @@ -1745,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 (); }