+/**\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