\r
#include <Library/UefiLib.h>\r
#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/DebugAgentLib.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
UINTN ApResetVectorSize;\r
EFI_PHYSICAL_ADDRESS StartAddress;\r
\r
- ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +\r
- sizeof (MP_CPU_EXCHANGE_INFO);\r
-\r
- StartAddress = BASE_1MB;\r
- Status = gBS->AllocatePages (\r
- AllocateMaxAddress,\r
- EfiACPIMemoryNVS,\r
- EFI_SIZE_TO_PAGES (ApResetVectorSize),\r
- &StartAddress\r
- );\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- CpuMpData->WakeupBuffer = (UINTN) StartAddress;\r
- CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)\r
+ if (CpuMpData->SaveRestoreFlag) {\r
+ BackupAndPrepareWakeupBuffer (CpuMpData);\r
+ } else {\r
+ ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +\r
+ sizeof (MP_CPU_EXCHANGE_INFO);\r
+\r
+ StartAddress = BASE_1MB;\r
+ Status = gBS->AllocatePages (\r
+ AllocateMaxAddress,\r
+ EfiACPIMemoryNVS,\r
+ EFI_SIZE_TO_PAGES (ApResetVectorSize),\r
+ &StartAddress\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ CpuMpData->WakeupBuffer = (UINTN) StartAddress;\r
+ CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)\r
(CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize);\r
- //\r
- // copy AP reset code in it\r
- //\r
- CopyMem (\r
- (VOID *) CpuMpData->WakeupBuffer,\r
- (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,\r
- CpuMpData->AddressMap.RendezvousFunnelSize\r
- );\r
+ //\r
+ // copy AP reset code in it\r
+ //\r
+ CopyMem (\r
+ (VOID *) CpuMpData->WakeupBuffer,\r
+ (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,\r
+ CpuMpData->AddressMap.RendezvousFunnelSize\r
+ );\r
+ }\r
}\r
\r
/**\r
{\r
EFI_STATUS Status;\r
UINTN ApResetVectorSize;\r
- ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +\r
- sizeof (MP_CPU_EXCHANGE_INFO);\r
- Status = gBS->FreePages(\r
- (EFI_PHYSICAL_ADDRESS)CpuMpData->WakeupBuffer,\r
- EFI_SIZE_TO_PAGES (ApResetVectorSize)\r
- );\r
- ASSERT_EFI_ERROR (Status);\r
+\r
+ if (CpuMpData->SaveRestoreFlag) {\r
+ RestoreWakeupBuffer (CpuMpData);\r
+ } else {\r
+ ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +\r
+ sizeof (MP_CPU_EXCHANGE_INFO);\r
+ Status = gBS->FreePages(\r
+ (EFI_PHYSICAL_ADDRESS)CpuMpData->WakeupBuffer,\r
+ EFI_SIZE_TO_PAGES (ApResetVectorSize)\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
}\r
\r
/**\r
/**\r
Checks APs' status periodically.\r
\r
- This function is triggerred by timer perodically to check the\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
}\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->SaveRestoreFlag = TRUE;\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
IN CPU_MP_DATA *CpuMpData\r
)\r
{\r
- EFI_STATUS Status;\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
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
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
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
)\r
{\r
EFI_STATUS Status;\r
- BOOLEAN OldInterruptState;\r
\r
- //\r
- // Before send both BSP and AP to a procedure to exchange their roles,\r
- // interrupt must be disabled. This is because during the exchange role\r
- // process, 2 CPU may use 1 stack. If interrupt happens, the stack will\r
- // be corrupted, since interrupt return address will be pushed to stack\r
- // by hardware.\r
- //\r
- OldInterruptState = SaveAndDisableInterrupts ();\r
-\r
- //\r
- // Mask LINT0 & LINT1 for the old BSP\r
- //\r
- DisableLvtInterrupts ();\r
\r
Status = SwitchBSPWorker (ProcessorNumber, EnableOldBSP);\r
\r
- //\r
- // Restore interrupt state.\r
- //\r
- SetInterruptState (OldInterruptState);\r
-\r
return Status;\r
}\r
\r