\r
#include <Library/UefiLib.h>\r
#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/DebugAgentLib.h>\r
+#include <Library/DxeServicesTableLib.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 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
\r
/**\r
- Allocate reset vector buffer.\r
-\r
- @param[in, out] CpuMpData The pointer to CPU MP Data structure.\r
-**/\r
-VOID\r
-AllocateResetVector (\r
- IN OUT CPU_MP_DATA *CpuMpData\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINTN ApResetVectorSize;\r
- EFI_PHYSICAL_ADDRESS StartAddress;\r
-\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
-}\r
+ Get available system memory below 1MB by specified size.\r
\r
-/**\r
- Free AP reset vector buffer.\r
+ @param[in] WakeupBufferSize Wakeup buffer size required\r
\r
- @param[in] CpuMpData The pointer to CPU MP Data structure.\r
+ @retval other Return wakeup buffer address below 1MB.\r
+ @retval -1 Cannot find free memory below 1MB.\r
**/\r
-VOID\r
-FreeResetVector (\r
- IN CPU_MP_DATA *CpuMpData\r
+UINTN\r
+GetWakeupBuffer (\r
+ IN UINTN WakeupBufferSize\r
)\r
{\r
- EFI_STATUS Status;\r
- UINTN ApResetVectorSize;\r
-\r
- if (CpuMpData->SaveRestoreFlag) {\r
- RestoreWakeupBuffer (CpuMpData);\r
- } else {\r
- ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +\r
- sizeof (MP_CPU_EXCHANGE_INFO);\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
- (EFI_PHYSICAL_ADDRESS)CpuMpData->WakeupBuffer,\r
- EFI_SIZE_TO_PAGES (ApResetVectorSize)\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
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) Buffer;\r
- AsmRelocateApLoopFunc (MwaitSupport, CpuMpData->ApTargetCState, CpuMpData->PmCodeSegment);\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
CPU_MP_DATA *CpuMpData;\r
\r
CpuMpData = GetCpuMpData ();\r
- CpuMpData->SaveRestoreFlag = TRUE;\r
CpuMpData->PmCodeSegment = GetProtectedModeCS ();\r
CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);\r
- WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, mReservedApLoopFunc);\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
IN CPU_MP_DATA *CpuMpData\r
)\r
{\r
- EFI_STATUS Status;\r
- EFI_PHYSICAL_ADDRESS Address;\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS Address;\r
+ UINTN ApSafeBufferSize;\r
+ UINTN Index;\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc;\r
+ UINTN StackBase;\r
+ CPU_INFO_IN_HOB *CpuInfoInHob;\r
\r
SaveCpuMpData (CpuMpData);\r
\r
return;\r
}\r
\r
+ if (PcdGetBool (PcdCpuStackGuard)) {\r
+ //\r
+ // One extra page at the bottom of the stack is needed for Guard page.\r
+ //\r
+ if (CpuMpData->CpuApStackSize <= EFI_PAGE_SIZE) {\r
+ DEBUG ((DEBUG_ERROR, "PcdCpuApStackSize is not big enough for Stack Guard!\n"));\r
+ ASSERT (FALSE);\r
+ }\r
+\r
+ //\r
+ // DXE will reuse stack allocated for APs at PEI phase if it's available.\r
+ // Let's check it here.\r
+ //\r
+ // Note: BSP's stack guard is set at DxeIpl phase. But for the sake of\r
+ // BSP/AP exchange, stack guard for ApTopOfStack of cpu 0 will still be\r
+ // set here.\r
+ //\r
+ CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;\r
+ for (Index = 0; Index < CpuMpData->CpuCount; ++Index) {\r
+ if (CpuInfoInHob != NULL && CpuInfoInHob[Index].ApTopOfStack != 0) {\r
+ StackBase = (UINTN)CpuInfoInHob[Index].ApTopOfStack - CpuMpData->CpuApStackSize;\r
+ } else {\r
+ StackBase = CpuMpData->Buffer + Index * CpuMpData->CpuApStackSize;\r
+ }\r
+\r
+ Status = gDS->GetMemorySpaceDescriptor (StackBase, &MemDesc);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = gDS->SetMemorySpaceAttributes (\r
+ StackBase,\r
+ EFI_PAGES_TO_SIZE (1),\r
+ MemDesc.Attributes | EFI_MEMORY_RP\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ DEBUG ((DEBUG_INFO, "Stack Guard set at %lx [cpu%lu]!\n",\r
+ (UINT64)StackBase, (UINT64)Index));\r
+ }\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
// 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 (sizeof (CpuMpData->AddressMap.RelocateApLoopFuncSize)),\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
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
IN BOOLEAN EnableOldBSP\r
)\r
{\r
- EFI_STATUS Status;\r
- BOOLEAN OldInterruptState;\r
+ EFI_STATUS Status;\r
+ EFI_TIMER_ARCH_PROTOCOL *Timer;\r
+ UINT64 TimerPeriod;\r
\r
+ TimerPeriod = 0;\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
+ // Locate Timer Arch Protocol\r
//\r
- OldInterruptState = SaveAndDisableInterrupts ();\r
+ Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **) &Timer);\r
+ if (EFI_ERROR (Status)) {\r
+ Timer = NULL;\r
+ }\r
\r
- //\r
- // Mask LINT0 & LINT1 for the old BSP\r
- //\r
- DisableLvtInterrupts ();\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
- //\r
- // Restore interrupt state.\r
- //\r
- SetInterruptState (OldInterruptState);\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