X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=UefiCpuPkg%2FLibrary%2FMpInitLib%2FDxeMpLib.c;h=e7ed21c6cd2d68268a6e5912724a2b3a791eaad5;hp=e459ebc5ebe4d4b5b63a7c44df2df52f7b968f8c;hb=bc2288f59ba2d613643ba66a9638bcc8c7340ddb;hpb=b31c1ad11e640eaa18cc32ceb095bc1ae5328ffa diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c index e459ebc5eb..e7ed21c6cd 100644 --- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c @@ -16,14 +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. @@ -53,63 +76,75 @@ 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 - ); + 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; } /** - Free AP reset vector buffer. + Get available EfiBootServicesCode memory below 4GB by specified size. - @param[in] CpuMpData The pointer to CPU MP Data structure. + This buffer is required to safely transfer AP from real address mode to + protected mode or long mode, due to the fact that the buffer returned by + GetWakeupBuffer() may be marked as non-executable. + + @param[in] BufferSize Wakeup transition buffer size. + + @retval other Return wakeup transition buffer address below 4GB. + @retval 0 Cannot find free memory below 4GB. **/ -VOID -FreeResetVector ( - IN CPU_MP_DATA *CpuMpData +UINTN +GetModeTransitionBuffer ( + IN UINTN BufferSize ) { - 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); + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS StartAddress; + + StartAddress = BASE_4GB - 1; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiBootServicesCode, + EFI_SIZE_TO_PAGES (BufferSize), + &StartAddress + ); + if (EFI_ERROR (Status)) { + StartAddress = 0; + } + + return (UINTN)StartAddress; } /** @@ -163,7 +198,7 @@ CheckAndUpdateApsStatus ( /** 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. @@ -231,11 +266,19 @@ RelocateApLoop ( 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) Buffer; - AsmRelocateApLoopFunc (MwaitSupport, CpuMpData->ApTargetCState, CpuMpData->PmCodeSegment); + 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 // @@ -252,7 +295,7 @@ RelocateApLoop ( **/ VOID EFIAPI -MpInitExitBootServicesCallback ( +MpInitChangeApLoopCallback ( IN EFI_EVENT Event, IN VOID *Context ) @@ -262,8 +305,12 @@ MpInitExitBootServicesCallback ( CpuMpData = GetCpuMpData (); CpuMpData->PmCodeSegment = GetProtectedModeCS (); CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode); - WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, mReservedApLoopFunc); - DEBUG ((DEBUG_INFO, "MpInitExitBootServicesCallback() done!\n")); + mNumberToFinish = CpuMpData->CpuCount - 1; + WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL); + while (mNumberToFinish > 0) { + CpuPause (); + } + DEBUG ((DEBUG_INFO, "%a() done!\n", __FUNCTION__)); } /** @@ -276,22 +323,122 @@ 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 = (UINTN)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 buff data which allocated by BootServices, - // so we will allocate reserved data for AP loop code. + // 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. // - mReservedApLoopFunc = AllocateReservedCopyPool ( - CpuMpData->AddressMap.RelocateApLoopFuncSize, - CpuMpData->AddressMap.RelocateApLoopFuncAddress - ); + ApSafeBufferSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES ( + CpuMpData->AddressMap.RelocateApLoopFuncSize + )); + 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); + // + // Make sure that the buffer memory is executable if NX protection is enabled + // for EfiReservedMemoryType. + // + // TODO: Check EFI_MEMORY_XP bit set or not once it's available in DXE GCD + // service. + // + Status = gDS->GetMemorySpaceDescriptor (Address, &MemDesc); + if (!EFI_ERROR (Status)) { + gDS->SetMemorySpaceAttributes ( + Address, + ApSafeBufferSize, + MemDesc.Attributes & (~EFI_MEMORY_XP) + ); + } + + ApSafeBufferSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES ( + 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); + + mReservedTopOfApStack = (UINTN) Address + 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, @@ -310,14 +457,25 @@ InitMpGlobalData ( AP_CHECK_INTERVAL ); ASSERT_EFI_ERROR (Status); + Status = gBS->CreateEvent ( EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK, - MpInitExitBootServicesCallback, + MpInitChangeApLoopCallback, NULL, &mMpInitExitBootServicesEvent ); ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + MpInitChangeApLoopCallback, + NULL, + &gEfiEventLegacyBootGuid, + &mLegacyBootEvent + ); + ASSERT_EFI_ERROR (Status); } /** @@ -347,7 +505,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 @@ -457,7 +615,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 @@ -566,29 +724,38 @@ MpInitLibSwitchBSP ( IN BOOLEAN EnableOldBSP ) { - EFI_STATUS Status; - BOOLEAN OldInterruptState; + EFI_STATUS Status; + EFI_TIMER_ARCH_PROTOCOL *Timer; + UINT64 TimerPeriod; + TimerPeriod = 0; // - // 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. + // Locate Timer Arch Protocol // - OldInterruptState = SaveAndDisableInterrupts (); + Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **) &Timer); + if (EFI_ERROR (Status)) { + Timer = NULL; + } - // - // Mask LINT0 & LINT1 for the old BSP - // - DisableLvtInterrupts (); + 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); - // - // Restore interrupt state. - // - SetInterruptState (OldInterruptState); + if (Timer != NULL) { + // + // Enable and restore rate of DXE Timer + // + Timer->SetTimerPeriod (Timer, TimerPeriod); + } return Status; }