X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=UefiCpuPkg%2FLibrary%2FMpInitLib%2FDxeMpLib.c;h=e7ed21c6cd2d68268a6e5912724a2b3a791eaad5;hp=50b5b270fe507cf262928d79751f339055f7bddc;hb=bc2288f59ba2d613643ba66a9638bcc8c7340ddb;hpb=3ed4e502b5f23fbcef235b3a9d025c60c217272b diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c index 50b5b270fe..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,72 +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; - - if (CpuMpData->SaveRestoreFlag) { - BackupAndPrepareWakeupBuffer (CpuMpData); - } else { - ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize + - sizeof (MP_CPU_EXCHANGE_INFO); - - StartAddress = BASE_1MB; - Status = gBS->AllocatePages ( - AllocateMaxAddress, - EfiACPIMemoryNVS, - EFI_SIZE_TO_PAGES (ApResetVectorSize), - &StartAddress - ); + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS StartAddress; + + StartAddress = BASE_1MB; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiBootServicesData, + EFI_SIZE_TO_PAGES (WakeupBufferSize), + &StartAddress + ); + ASSERT_EFI_ERROR (Status); + if (!EFI_ERROR (Status)) { + Status = gBS->FreePages( + StartAddress, + EFI_SIZE_TO_PAGES (WakeupBufferSize) + ); 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 - ); + 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. + + 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. - @param[in] CpuMpData The pointer to CPU MP Data structure. + @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; - - if (CpuMpData->SaveRestoreFlag) { - RestoreWakeupBuffer (CpuMpData); - } else { - 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; } /** @@ -172,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. @@ -240,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 // @@ -261,7 +295,7 @@ RelocateApLoop ( **/ VOID EFIAPI -MpInitExitBootServicesCallback ( +MpInitChangeApLoopCallback ( IN EFI_EVENT Event, IN VOID *Context ) @@ -269,11 +303,14 @@ MpInitExitBootServicesCallback ( CPU_MP_DATA *CpuMpData; CpuMpData = GetCpuMpData (); - CpuMpData->SaveRestoreFlag = TRUE; 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__)); } /** @@ -286,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, @@ -320,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); } /** @@ -357,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 @@ -467,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 @@ -576,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; }