X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=UefiCpuPkg%2FLibrary%2FMpInitLib%2FDxeMpLib.c;h=e832c16ecacec771b7a5be37a77520a9942b17c6;hp=762d76dd7a7f2d774dc004acd1f8bc1ab20ce511;hb=523152618d20a58c9ad55203fa6e5b8bebe938f0;hpb=ed66e0e3f48db7a97ca82b8b630b0cf83b1283fb diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c index 762d76dd7a..e832c16eca 100644 --- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c @@ -16,13 +16,37 @@ #include #include +#include +#include + +#include #define AP_CHECK_INTERVAL (EFI_TIMER_PERIOD_MILLISECONDS (100)) +#define AP_SAFE_STACK_SIZE 128 CPU_MP_DATA *mCpuMpData = NULL; EFI_EVENT mCheckAllApsEvent = NULL; +EFI_EVENT mMpInitExitBootServicesEvent = NULL; +EFI_EVENT mLegacyBootEvent = NULL; volatile BOOLEAN mStopCheckAllApsStatus = TRUE; +VOID *mReservedApLoopFunc = NULL; +UINTN mReservedTopOfApStack; +volatile UINT32 mNumberToFinish = 0; + +/** + Enable Debug Agent to support source debugging on AP function. +**/ +VOID +EnableDebugAgent ( + VOID + ) +{ + // + // Initialize Debug Agent to support source level debug in DXE phase + // + InitializeDebugAgent (DEBUG_AGENT_INIT_DXE_AP, NULL, NULL); +} /** Get the pointer to CPU MP Data structure. @@ -52,63 +76,41 @@ SaveCpuMpData ( } /** - Allocate reset vector buffer. + Get available system memory below 1MB by specified size. + + @param[in] WakeupBufferSize Wakeup buffer size required - @param[in, out] CpuMpData The pointer to CPU MP Data structure. + @retval other Return wakeup buffer address below 1MB. + @retval -1 Cannot find free memory below 1MB. **/ -VOID -AllocateResetVector ( - IN OUT CPU_MP_DATA *CpuMpData +UINTN +GetWakeupBuffer ( + IN UINTN WakeupBufferSize ) { - EFI_STATUS Status; - UINTN ApResetVectorSize; - EFI_PHYSICAL_ADDRESS StartAddress; - - ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize + - sizeof (MP_CPU_EXCHANGE_INFO); + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS StartAddress; StartAddress = BASE_1MB; Status = gBS->AllocatePages ( AllocateMaxAddress, - EfiACPIMemoryNVS, - EFI_SIZE_TO_PAGES (ApResetVectorSize), + EfiBootServicesData, + EFI_SIZE_TO_PAGES (WakeupBufferSize), &StartAddress ); ASSERT_EFI_ERROR (Status); - - CpuMpData->WakeupBuffer = (UINTN) StartAddress; - CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) - (CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize); - // - // copy AP reset code in it - // - CopyMem ( - (VOID *) CpuMpData->WakeupBuffer, - (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress, - CpuMpData->AddressMap.RendezvousFunnelSize - ); -} - -/** - Free AP reset vector buffer. - - @param[in] CpuMpData The pointer to CPU MP Data structure. -**/ -VOID -FreeResetVector ( - IN CPU_MP_DATA *CpuMpData - ) -{ - EFI_STATUS Status; - UINTN ApResetVectorSize; - ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize + - sizeof (MP_CPU_EXCHANGE_INFO); - Status = gBS->FreePages( - (EFI_PHYSICAL_ADDRESS)CpuMpData->WakeupBuffer, - EFI_SIZE_TO_PAGES (ApResetVectorSize) - ); - ASSERT_EFI_ERROR (Status); + if (!EFI_ERROR (Status)) { + Status = gBS->FreePages( + StartAddress, + EFI_SIZE_TO_PAGES (WakeupBufferSize) + ); + ASSERT_EFI_ERROR (Status); + DEBUG ((DEBUG_INFO, "WakeupBufferStart = %x, WakeupBufferSize = %x\n", + (UINTN) StartAddress, WakeupBufferSize)); + } else { + StartAddress = (EFI_PHYSICAL_ADDRESS) -1; + } + return (UINTN) StartAddress; } /** @@ -120,12 +122,49 @@ CheckAndUpdateApsStatus ( VOID ) { + UINTN ProcessorNumber; + EFI_STATUS Status; + CPU_MP_DATA *CpuMpData; + + CpuMpData = GetCpuMpData (); + + // + // First, check whether pending StartupAllAPs() exists. + // + if (CpuMpData->WaitEvent != NULL) { + + Status = CheckAllAPs (); + // + // If all APs finish for StartupAllAPs(), signal the WaitEvent for it. + // + if (Status != EFI_NOT_READY) { + Status = gBS->SignalEvent (CpuMpData->WaitEvent); + CpuMpData->WaitEvent = NULL; + } + } + + // + // Second, check whether pending StartupThisAPs() callings exist. + // + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { + + if (CpuMpData->CpuData[ProcessorNumber].WaitEvent == NULL) { + continue; + } + + Status = CheckThisAP (ProcessorNumber); + + if (Status != EFI_NOT_READY) { + gBS->SignalEvent (CpuMpData->CpuData[ProcessorNumber].WaitEvent); + CpuMpData->CpuData[ProcessorNumber].WaitEvent = NULL; + } + } } /** Checks APs' status periodically. - This function is triggerred by timer perodically to check the + This function is triggered by timer periodically to check the state of APs for StartupAllAPs() and StartupThisAP() executed in non-blocking mode. @@ -148,6 +187,98 @@ CheckApsStatus ( } } +/** + Get Protected mode code segment from current GDT table. + + @return Protected mode code segment value. +**/ +UINT16 +GetProtectedModeCS ( + VOID + ) +{ + IA32_DESCRIPTOR GdtrDesc; + IA32_SEGMENT_DESCRIPTOR *GdtEntry; + UINTN GdtEntryCount; + UINT16 Index; + + Index = (UINT16) -1; + AsmReadGdtr (&GdtrDesc); + GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR); + GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base; + for (Index = 0; Index < GdtEntryCount; Index++) { + if (GdtEntry->Bits.L == 0) { + if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) { + break; + } + } + GdtEntry++; + } + ASSERT (Index != -1); + return Index * 8; +} + +/** + Do sync on APs. + + @param[in, out] Buffer Pointer to private data buffer. +**/ +VOID +EFIAPI +RelocateApLoop ( + IN OUT VOID *Buffer + ) +{ + CPU_MP_DATA *CpuMpData; + BOOLEAN MwaitSupport; + ASM_RELOCATE_AP_LOOP AsmRelocateApLoopFunc; + UINTN ProcessorNumber; + + MpInitLibWhoAmI (&ProcessorNumber); + CpuMpData = GetCpuMpData (); + MwaitSupport = IsMwaitSupport (); + AsmRelocateApLoopFunc = (ASM_RELOCATE_AP_LOOP) (UINTN) mReservedApLoopFunc; + AsmRelocateApLoopFunc ( + MwaitSupport, + CpuMpData->ApTargetCState, + CpuMpData->PmCodeSegment, + mReservedTopOfApStack - ProcessorNumber * AP_SAFE_STACK_SIZE, + (UINTN) &mNumberToFinish + ); + // + // It should never reach here + // + ASSERT (FALSE); +} + +/** + Callback function for ExitBootServices. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context The pointer to the notification function's context, + which is implementation-dependent. + +**/ +VOID +EFIAPI +MpInitChangeApLoopCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + CPU_MP_DATA *CpuMpData; + + CpuMpData = GetCpuMpData (); + CpuMpData->PmCodeSegment = GetProtectedModeCS (); + CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode); + mNumberToFinish = CpuMpData->CpuCount - 1; + WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL); + while (mNumberToFinish > 0) { + CpuPause (); + } + DEBUG ((DEBUG_INFO, "%a() done!\n", __FUNCTION__)); +} + /** Initialize global data for MP support. @@ -158,10 +289,92 @@ InitMpGlobalData ( IN CPU_MP_DATA *CpuMpData ) { - EFI_STATUS Status; + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Address; + UINTN ApSafeBufferSize; + UINTN Index; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc; + UINTN StackBase; + CPU_INFO_IN_HOB *CpuInfoInHob; SaveCpuMpData (CpuMpData); + if (CpuMpData->CpuCount == 1) { + // + // If only BSP exists, return + // + return; + } + + if (PcdGetBool (PcdCpuStackGuard)) { + // + // One extra page at the bottom of the stack is needed for Guard page. + // + if (CpuMpData->CpuApStackSize <= EFI_PAGE_SIZE) { + DEBUG ((DEBUG_ERROR, "PcdCpuApStackSize is not big enough for Stack Guard!\n")); + ASSERT (FALSE); + } + + // + // DXE will reuse stack allocated for APs at PEI phase if it's available. + // Let's check it here. + // + // Note: BSP's stack guard is set at DxeIpl phase. But for the sake of + // BSP/AP exchange, stack guard for ApTopOfStack of cpu 0 will still be + // set here. + // + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; + for (Index = 0; Index < CpuMpData->CpuCount; ++Index) { + if (CpuInfoInHob != NULL && CpuInfoInHob[Index].ApTopOfStack != 0) { + StackBase = CpuInfoInHob[Index].ApTopOfStack - CpuMpData->CpuApStackSize; + } else { + StackBase = CpuMpData->Buffer + Index * CpuMpData->CpuApStackSize; + } + + Status = gDS->GetMemorySpaceDescriptor (StackBase, &MemDesc); + ASSERT_EFI_ERROR (Status); + + Status = gDS->SetMemorySpaceAttributes ( + StackBase, + EFI_PAGES_TO_SIZE (1), + MemDesc.Attributes | EFI_MEMORY_RP + ); + ASSERT_EFI_ERROR (Status); + + DEBUG ((DEBUG_INFO, "Stack Guard set at %lx [cpu%lu]!\n", + (UINT64)StackBase, (UINT64)Index)); + } + } + + // + // Avoid APs access invalid buffer data which allocated by BootServices, + // so we will allocate reserved data for AP loop code. We also need to + // allocate this buffer below 4GB due to APs may be transferred to 32bit + // protected mode on long mode DXE. + // Allocating it in advance since memory services are not available in + // Exit Boot Services callback function. + // + ApSafeBufferSize = CpuMpData->AddressMap.RelocateApLoopFuncSize; + ApSafeBufferSize += CpuMpData->CpuCount * AP_SAFE_STACK_SIZE; + + Address = BASE_4GB - 1; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiReservedMemoryType, + EFI_SIZE_TO_PAGES (ApSafeBufferSize), + &Address + ); + ASSERT_EFI_ERROR (Status); + mReservedApLoopFunc = (VOID *) (UINTN) Address; + ASSERT (mReservedApLoopFunc != NULL); + mReservedTopOfApStack = (UINTN) Address + EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (ApSafeBufferSize)); + ASSERT ((mReservedTopOfApStack & (UINTN)(CPU_STACK_ALIGNMENT - 1)) == 0); + CopyMem ( + mReservedApLoopFunc, + CpuMpData->AddressMap.RelocateApLoopFuncAddress, + CpuMpData->AddressMap.RelocateApLoopFuncSize + ); + Status = gBS->CreateEvent ( EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, @@ -180,6 +393,25 @@ InitMpGlobalData ( AP_CHECK_INTERVAL ); ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEvent ( + EVT_SIGNAL_EXIT_BOOT_SERVICES, + TPL_CALLBACK, + MpInitChangeApLoopCallback, + NULL, + &mMpInitExitBootServicesEvent + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + MpInitChangeApLoopCallback, + NULL, + &gEfiEventLegacyBootGuid, + &mLegacyBootEvent + ); + ASSERT_EFI_ERROR (Status); } /** @@ -209,7 +441,7 @@ InitMpGlobalData ( EFI_EVENT is defined in CreateEvent() in the Unified Extensible Firmware Interface Specification. - @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. Zero means infinity. If the timeout expires before @@ -268,7 +500,28 @@ MpInitLibStartupAllAPs ( OUT UINTN **FailedCpuList OPTIONAL ) { - return EFI_UNSUPPORTED; + EFI_STATUS Status; + + // + // Temporarily stop checkAllApsStatus for avoid resource dead-lock. + // + mStopCheckAllApsStatus = TRUE; + + Status = StartupAllAPsWorker ( + Procedure, + SingleThread, + WaitEvent, + TimeoutInMicroseconds, + ProcedureArgument, + FailedCpuList + ); + + // + // Start checkAllApsStatus + // + mStopCheckAllApsStatus = FALSE; + + return Status; } /** @@ -298,7 +551,7 @@ MpInitLibStartupAllAPs ( EFI_EVENT is defined in CreateEvent() in the Unified Extensible Firmware Interface Specification. - @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for this AP to finish this Procedure, either for blocking or non-blocking mode. Zero means infinity. If the timeout expires before @@ -353,7 +606,25 @@ MpInitLibStartupThisAP ( OUT BOOLEAN *Finished OPTIONAL ) { - return EFI_UNSUPPORTED; + EFI_STATUS Status; + + // + // temporarily stop checkAllApsStatus for avoid resource dead-lock. + // + mStopCheckAllApsStatus = TRUE; + + Status = StartupThisAPWorker ( + Procedure, + ProcessorNumber, + WaitEvent, + TimeoutInMicroseconds, + ProcedureArgument, + Finished + ); + + mStopCheckAllApsStatus = FALSE; + + return Status; } /** @@ -389,7 +660,40 @@ MpInitLibSwitchBSP ( IN BOOLEAN EnableOldBSP ) { - return EFI_UNSUPPORTED; + EFI_STATUS Status; + EFI_TIMER_ARCH_PROTOCOL *Timer; + UINT64 TimerPeriod; + + TimerPeriod = 0; + // + // Locate Timer Arch Protocol + // + Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **) &Timer); + if (EFI_ERROR (Status)) { + Timer = NULL; + } + + if (Timer != NULL) { + // + // Save current rate of DXE Timer + // + Timer->GetTimerPeriod (Timer, &TimerPeriod); + // + // Disable DXE Timer and drain pending interrupts + // + Timer->SetTimerPeriod (Timer, 0); + } + + Status = SwitchBSPWorker (ProcessorNumber, EnableOldBSP); + + if (Timer != NULL) { + // + // Enable and restore rate of DXE Timer + // + Timer->SetTimerPeriod (Timer, TimerPeriod); + } + + return Status; } /** @@ -430,5 +734,23 @@ MpInitLibEnableDisableAP ( IN UINT32 *HealthFlag OPTIONAL ) { - return EFI_UNSUPPORTED; + EFI_STATUS Status; + BOOLEAN TempStopCheckState; + + TempStopCheckState = FALSE; + // + // temporarily stop checkAllAPsStatus for initialize parameters. + // + if (!mStopCheckAllApsStatus) { + mStopCheckAllApsStatus = TRUE; + TempStopCheckState = TRUE; + } + + Status = EnableDisableApWorker (ProcessorNumber, EnableAP, HealthFlag); + + if (TempStopCheckState) { + mStopCheckAllApsStatus = FALSE; + } + + return Status; }