\r
#include "MpLib.h"\r
\r
+#include <Library/UefiLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/DebugAgentLib.h>\r
+\r
+#include <Protocol/Timer.h>\r
+\r
+#define AP_CHECK_INTERVAL (EFI_TIMER_PERIOD_MILLISECONDS (100))\r
+#define AP_SAFE_STACK_SIZE 128\r
+\r
+CPU_MP_DATA *mCpuMpData = NULL;\r
+EFI_EVENT mCheckAllApsEvent = NULL;\r
+EFI_EVENT mMpInitExitBootServicesEvent = NULL;\r
+EFI_EVENT mLegacyBootEvent = NULL;\r
+volatile BOOLEAN mStopCheckAllApsStatus = TRUE;\r
+VOID *mReservedApLoopFunc = NULL;\r
+UINTN mReservedTopOfApStack;\r
+volatile UINT32 mNumberToFinish = 0;\r
+\r
+/**\r
+ Enable Debug Agent to support source debugging on AP function.\r
+\r
+**/\r
+VOID\r
+EnableDebugAgent (\r
+ VOID\r
+ )\r
+{\r
+ //\r
+ // Initialize Debug Agent to support source level debug in DXE phase\r
+ //\r
+ InitializeDebugAgent (DEBUG_AGENT_INIT_DXE_AP, NULL, NULL);\r
+}\r
+\r
+/**\r
+ Get the pointer to CPU MP Data structure.\r
+\r
+ @return The pointer to CPU MP Data structure.\r
+**/\r
+CPU_MP_DATA *\r
+GetCpuMpData (\r
+ VOID\r
+ )\r
+{\r
+ ASSERT (mCpuMpData != NULL);\r
+ return mCpuMpData;\r
+}\r
+\r
+/**\r
+ Save the pointer to CPU MP Data structure.\r
+\r
+ @param[in] CpuMpData The pointer to CPU MP Data structure will be saved.\r
+**/\r
+VOID\r
+SaveCpuMpData (\r
+ IN CPU_MP_DATA *CpuMpData\r
+ )\r
+{\r
+ mCpuMpData = CpuMpData;\r
+}\r
+\r
+/**\r
+ Get available system memory below 1MB by specified size.\r
+\r
+ @param[in] WakeupBufferSize Wakeup buffer size required\r
+\r
+ @retval other Return wakeup buffer address below 1MB.\r
+ @retval -1 Cannot find free memory below 1MB.\r
+**/\r
+UINTN\r
+GetWakeupBuffer (\r
+ IN UINTN WakeupBufferSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS StartAddress;\r
+\r
+ StartAddress = BASE_1MB;\r
+ Status = gBS->AllocatePages (\r
+ AllocateMaxAddress,\r
+ EfiBootServicesData,\r
+ EFI_SIZE_TO_PAGES (WakeupBufferSize),\r
+ &StartAddress\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = gBS->FreePages(\r
+ StartAddress,\r
+ EFI_SIZE_TO_PAGES (WakeupBufferSize)\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ DEBUG ((DEBUG_INFO, "WakeupBufferStart = %x, WakeupBufferSize = %x\n",\r
+ (UINTN) StartAddress, WakeupBufferSize));\r
+ } else {\r
+ StartAddress = (EFI_PHYSICAL_ADDRESS) -1;\r
+ }\r
+ return (UINTN) StartAddress;\r
+}\r
+\r
+/**\r
+ Checks APs status and updates APs status if needed.\r
+\r
+**/\r
+VOID\r
+CheckAndUpdateApsStatus (\r
+ VOID\r
+ )\r
+{\r
+ UINTN ProcessorNumber;\r
+ EFI_STATUS Status;\r
+ CPU_MP_DATA *CpuMpData;\r
+\r
+ CpuMpData = GetCpuMpData ();\r
+\r
+ //\r
+ // First, check whether pending StartupAllAPs() exists.\r
+ //\r
+ if (CpuMpData->WaitEvent != NULL) {\r
+\r
+ Status = CheckAllAPs ();\r
+ //\r
+ // If all APs finish for StartupAllAPs(), signal the WaitEvent for it.\r
+ //\r
+ if (Status != EFI_NOT_READY) {\r
+ Status = gBS->SignalEvent (CpuMpData->WaitEvent);\r
+ CpuMpData->WaitEvent = NULL;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Second, check whether pending StartupThisAPs() callings exist.\r
+ //\r
+ for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) {\r
+\r
+ if (CpuMpData->CpuData[ProcessorNumber].WaitEvent == NULL) {\r
+ continue;\r
+ }\r
+\r
+ Status = CheckThisAP (ProcessorNumber);\r
+\r
+ if (Status != EFI_NOT_READY) {\r
+ gBS->SignalEvent (CpuMpData->CpuData[ProcessorNumber].WaitEvent);\r
+ CpuMpData->CpuData[ProcessorNumber].WaitEvent = NULL;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Checks APs' status periodically.\r
+\r
+ This function is triggered by timer periodically to check the\r
+ state of APs for StartupAllAPs() and StartupThisAP() executed\r
+ in non-blocking mode.\r
+\r
+ @param[in] Event Event triggered.\r
+ @param[in] Context Parameter passed with the event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+CheckApsStatus (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ //\r
+ // If CheckApsStatus() is not stopped, otherwise return immediately.\r
+ //\r
+ if (!mStopCheckAllApsStatus) {\r
+ CheckAndUpdateApsStatus ();\r
+ }\r
+}\r
+\r
+/**\r
+ Get Protected mode code segment from current GDT table.\r
+\r
+ @return Protected mode code segment value.\r
+**/\r
+UINT16\r
+GetProtectedModeCS (\r
+ VOID\r
+ )\r
+{\r
+ IA32_DESCRIPTOR GdtrDesc;\r
+ IA32_SEGMENT_DESCRIPTOR *GdtEntry;\r
+ UINTN GdtEntryCount;\r
+ UINT16 Index;\r
+\r
+ Index = (UINT16) -1;\r
+ AsmReadGdtr (&GdtrDesc);\r
+ GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
+ GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;\r
+ for (Index = 0; Index < GdtEntryCount; Index++) {\r
+ if (GdtEntry->Bits.L == 0) {\r
+ if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {\r
+ break;\r
+ }\r
+ }\r
+ GdtEntry++;\r
+ }\r
+ ASSERT (Index != -1);\r
+ return Index * 8;\r
+}\r
+\r
+/**\r
+ Do sync on APs.\r
+\r
+ @param[in, out] Buffer Pointer to private data buffer.\r
+**/\r
+VOID\r
+EFIAPI\r
+RelocateApLoop (\r
+ IN OUT VOID *Buffer\r
+ )\r
+{\r
+ CPU_MP_DATA *CpuMpData;\r
+ BOOLEAN MwaitSupport;\r
+ ASM_RELOCATE_AP_LOOP AsmRelocateApLoopFunc;\r
+ UINTN ProcessorNumber;\r
+\r
+ MpInitLibWhoAmI (&ProcessorNumber); \r
+ CpuMpData = GetCpuMpData ();\r
+ MwaitSupport = IsMwaitSupport ();\r
+ AsmRelocateApLoopFunc = (ASM_RELOCATE_AP_LOOP) (UINTN) mReservedApLoopFunc;\r
+ AsmRelocateApLoopFunc (\r
+ MwaitSupport,\r
+ CpuMpData->ApTargetCState,\r
+ CpuMpData->PmCodeSegment,\r
+ mReservedTopOfApStack - ProcessorNumber * AP_SAFE_STACK_SIZE,\r
+ (UINTN) &mNumberToFinish\r
+ );\r
+ //\r
+ // It should never reach here\r
+ //\r
+ ASSERT (FALSE);\r
+}\r
+\r
+/**\r
+ Callback function for ExitBootServices.\r
+\r
+ @param[in] Event Event whose notification function is being invoked.\r
+ @param[in] Context The pointer to the notification function's context,\r
+ which is implementation-dependent.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+MpInitChangeApLoopCallback (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ CPU_MP_DATA *CpuMpData;\r
+\r
+ CpuMpData = GetCpuMpData ();\r
+ CpuMpData->PmCodeSegment = GetProtectedModeCS ();\r
+ CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);\r
+ mNumberToFinish = CpuMpData->CpuCount - 1;\r
+ WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL);\r
+ while (mNumberToFinish > 0) {\r
+ CpuPause ();\r
+ }\r
+ DEBUG ((DEBUG_INFO, "%a() done!\n", __FUNCTION__));\r
+}\r
+\r
+/**\r
+ Initialize global data for MP support.\r
+\r
+ @param[in] CpuMpData The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+InitMpGlobalData (\r
+ IN CPU_MP_DATA *CpuMpData\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS Address;\r
+ UINTN ApSafeBufferSize;\r
+\r
+ SaveCpuMpData (CpuMpData);\r
+\r
+ if (CpuMpData->CpuCount == 1) {\r
+ //\r
+ // If only BSP exists, return\r
+ //\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Avoid APs access invalid buffer data which allocated by BootServices,\r
+ // so we will allocate reserved data for AP loop code. We also need to\r
+ // allocate this buffer below 4GB due to APs may be transferred to 32bit\r
+ // protected mode on long mode DXE.\r
+ // Allocating it in advance since memory services are not available in\r
+ // Exit Boot Services callback function.\r
+ //\r
+ ApSafeBufferSize = CpuMpData->AddressMap.RelocateApLoopFuncSize;\r
+ ApSafeBufferSize += CpuMpData->CpuCount * AP_SAFE_STACK_SIZE;\r
+\r
+ Address = BASE_4GB - 1;\r
+ Status = gBS->AllocatePages (\r
+ AllocateMaxAddress,\r
+ EfiReservedMemoryType,\r
+ EFI_SIZE_TO_PAGES (ApSafeBufferSize),\r
+ &Address\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ mReservedApLoopFunc = (VOID *) (UINTN) Address;\r
+ ASSERT (mReservedApLoopFunc != NULL);\r
+ mReservedTopOfApStack = (UINTN) Address + EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (ApSafeBufferSize));\r
+ ASSERT ((mReservedTopOfApStack & (UINTN)(CPU_STACK_ALIGNMENT - 1)) == 0);\r
+ CopyMem (\r
+ mReservedApLoopFunc,\r
+ CpuMpData->AddressMap.RelocateApLoopFuncAddress,\r
+ CpuMpData->AddressMap.RelocateApLoopFuncSize\r
+ );\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ CheckApsStatus,\r
+ NULL,\r
+ &mCheckAllApsEvent\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Set timer to check all APs status.\r
+ //\r
+ Status = gBS->SetTimer (\r
+ mCheckAllApsEvent,\r
+ TimerPeriodic,\r
+ AP_CHECK_INTERVAL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_SIGNAL_EXIT_BOOT_SERVICES,\r
+ TPL_CALLBACK,\r
+ MpInitChangeApLoopCallback,\r
+ NULL,\r
+ &mMpInitExitBootServicesEvent\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = gBS->CreateEventEx (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ MpInitChangeApLoopCallback,\r
+ NULL,\r
+ &gEfiEventLegacyBootGuid,\r
+ &mLegacyBootEvent\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+}\r
+\r
/**\r
This service executes a caller provided function on all enabled APs.\r
\r
EFI_EVENT is defined in CreateEvent() in\r
the Unified Extensible Firmware Interface\r
Specification.\r
- @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for\r
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
APs to return from Procedure, either for\r
blocking or non-blocking mode. Zero means\r
infinity. If the timeout expires before\r
OUT UINTN **FailedCpuList OPTIONAL\r
)\r
{\r
- return EFI_UNSUPPORTED;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Temporarily stop checkAllApsStatus for avoid resource dead-lock.\r
+ //\r
+ mStopCheckAllApsStatus = TRUE;\r
+\r
+ Status = StartupAllAPsWorker (\r
+ Procedure,\r
+ SingleThread,\r
+ WaitEvent,\r
+ TimeoutInMicroseconds,\r
+ ProcedureArgument,\r
+ FailedCpuList\r
+ );\r
+\r
+ //\r
+ // Start checkAllApsStatus\r
+ //\r
+ mStopCheckAllApsStatus = FALSE;\r
+\r
+ return Status;\r
}\r
\r
/**\r
EFI_EVENT is defined in CreateEvent() in\r
the Unified Extensible Firmware Interface\r
Specification.\r
- @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for\r
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
this AP to finish this Procedure, either for\r
blocking or non-blocking mode. Zero means\r
infinity. If the timeout expires before\r
OUT BOOLEAN *Finished OPTIONAL\r
)\r
{\r
- return EFI_UNSUPPORTED;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // temporarily stop checkAllApsStatus for avoid resource dead-lock.\r
+ //\r
+ mStopCheckAllApsStatus = TRUE;\r
+\r
+ Status = StartupThisAPWorker (\r
+ Procedure,\r
+ ProcessorNumber,\r
+ WaitEvent,\r
+ TimeoutInMicroseconds,\r
+ ProcedureArgument,\r
+ Finished\r
+ );\r
+\r
+ mStopCheckAllApsStatus = FALSE;\r
+\r
+ return Status;\r
}\r
\r
/**\r
IN BOOLEAN EnableOldBSP\r
)\r
{\r
- return EFI_UNSUPPORTED;\r
+ EFI_STATUS Status;\r
+ EFI_TIMER_ARCH_PROTOCOL *Timer;\r
+ UINT64 TimerPeriod;\r
+\r
+ TimerPeriod = 0;\r
+ //\r
+ // Locate Timer Arch Protocol\r
+ //\r
+ Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **) &Timer);\r
+ if (EFI_ERROR (Status)) {\r
+ Timer = NULL;\r
+ }\r
+\r
+ if (Timer != NULL) {\r
+ //\r
+ // Save current rate of DXE Timer\r
+ //\r
+ Timer->GetTimerPeriod (Timer, &TimerPeriod);\r
+ //\r
+ // Disable DXE Timer and drain pending interrupts\r
+ //\r
+ Timer->SetTimerPeriod (Timer, 0);\r
+ }\r
+\r
+ Status = SwitchBSPWorker (ProcessorNumber, EnableOldBSP);\r
+\r
+ if (Timer != NULL) {\r
+ //\r
+ // Enable and restore rate of DXE Timer\r
+ //\r
+ Timer->SetTimerPeriod (Timer, TimerPeriod);\r
+ }\r
+\r
+ return Status;\r
}\r
\r
/**\r
IN UINT32 *HealthFlag OPTIONAL\r
)\r
{\r
- return EFI_UNSUPPORTED;\r
+ EFI_STATUS Status;\r
+ BOOLEAN TempStopCheckState;\r
+\r
+ TempStopCheckState = FALSE;\r
+ //\r
+ // temporarily stop checkAllAPsStatus for initialize parameters.\r
+ //\r
+ if (!mStopCheckAllApsStatus) {\r
+ mStopCheckAllApsStatus = TRUE;\r
+ TempStopCheckState = TRUE;\r
+ }\r
+\r
+ Status = EnableDisableApWorker (ProcessorNumber, EnableAP, HealthFlag);\r
+\r
+ if (TempStopCheckState) {\r
+ mStopCheckAllApsStatus = FALSE;\r
+ }\r
+\r
+ return Status;\r
}\r