2 MP initialize support functions for DXE phase.
4 Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include <Library/UefiLib.h>
18 #include <Library/UefiBootServicesTableLib.h>
19 #include <Library/DebugAgentLib.h>
20 #include <Library/DxeServicesTableLib.h>
22 #include <Protocol/Timer.h>
24 #define AP_CHECK_INTERVAL (EFI_TIMER_PERIOD_MILLISECONDS (100))
25 #define AP_SAFE_STACK_SIZE 128
27 CPU_MP_DATA
*mCpuMpData
= NULL
;
28 EFI_EVENT mCheckAllApsEvent
= NULL
;
29 EFI_EVENT mMpInitExitBootServicesEvent
= NULL
;
30 EFI_EVENT mLegacyBootEvent
= NULL
;
31 volatile BOOLEAN mStopCheckAllApsStatus
= TRUE
;
32 VOID
*mReservedApLoopFunc
= NULL
;
33 UINTN mReservedTopOfApStack
;
34 volatile UINT32 mNumberToFinish
= 0;
37 Enable Debug Agent to support source debugging on AP function.
46 // Initialize Debug Agent to support source level debug in DXE phase
48 InitializeDebugAgent (DEBUG_AGENT_INIT_DXE_AP
, NULL
, NULL
);
52 Get the pointer to CPU MP Data structure.
54 @return The pointer to CPU MP Data structure.
61 ASSERT (mCpuMpData
!= NULL
);
66 Save the pointer to CPU MP Data structure.
68 @param[in] CpuMpData The pointer to CPU MP Data structure will be saved.
72 IN CPU_MP_DATA
*CpuMpData
75 mCpuMpData
= CpuMpData
;
79 Get available system memory below 1MB by specified size.
81 @param[in] WakeupBufferSize Wakeup buffer size required
83 @retval other Return wakeup buffer address below 1MB.
84 @retval -1 Cannot find free memory below 1MB.
88 IN UINTN WakeupBufferSize
92 EFI_PHYSICAL_ADDRESS StartAddress
;
94 StartAddress
= BASE_1MB
;
95 Status
= gBS
->AllocatePages (
98 EFI_SIZE_TO_PAGES (WakeupBufferSize
),
101 ASSERT_EFI_ERROR (Status
);
102 if (!EFI_ERROR (Status
)) {
103 Status
= gBS
->FreePages(
105 EFI_SIZE_TO_PAGES (WakeupBufferSize
)
107 ASSERT_EFI_ERROR (Status
);
108 DEBUG ((DEBUG_INFO
, "WakeupBufferStart = %x, WakeupBufferSize = %x\n",
109 (UINTN
) StartAddress
, WakeupBufferSize
));
111 StartAddress
= (EFI_PHYSICAL_ADDRESS
) -1;
113 return (UINTN
) StartAddress
;
117 Checks APs status and updates APs status if needed.
121 CheckAndUpdateApsStatus (
125 UINTN ProcessorNumber
;
127 CPU_MP_DATA
*CpuMpData
;
129 CpuMpData
= GetCpuMpData ();
132 // First, check whether pending StartupAllAPs() exists.
134 if (CpuMpData
->WaitEvent
!= NULL
) {
136 Status
= CheckAllAPs ();
138 // If all APs finish for StartupAllAPs(), signal the WaitEvent for it.
140 if (Status
!= EFI_NOT_READY
) {
141 Status
= gBS
->SignalEvent (CpuMpData
->WaitEvent
);
142 CpuMpData
->WaitEvent
= NULL
;
147 // Second, check whether pending StartupThisAPs() callings exist.
149 for (ProcessorNumber
= 0; ProcessorNumber
< CpuMpData
->CpuCount
; ProcessorNumber
++) {
151 if (CpuMpData
->CpuData
[ProcessorNumber
].WaitEvent
== NULL
) {
155 Status
= CheckThisAP (ProcessorNumber
);
157 if (Status
!= EFI_NOT_READY
) {
158 gBS
->SignalEvent (CpuMpData
->CpuData
[ProcessorNumber
].WaitEvent
);
159 CpuMpData
->CpuData
[ProcessorNumber
].WaitEvent
= NULL
;
165 Checks APs' status periodically.
167 This function is triggered by timer periodically to check the
168 state of APs for StartupAllAPs() and StartupThisAP() executed
169 in non-blocking mode.
171 @param[in] Event Event triggered.
172 @param[in] Context Parameter passed with the event.
183 // If CheckApsStatus() is not stopped, otherwise return immediately.
185 if (!mStopCheckAllApsStatus
) {
186 CheckAndUpdateApsStatus ();
191 Get Protected mode code segment from current GDT table.
193 @return Protected mode code segment value.
200 IA32_DESCRIPTOR GdtrDesc
;
201 IA32_SEGMENT_DESCRIPTOR
*GdtEntry
;
206 AsmReadGdtr (&GdtrDesc
);
207 GdtEntryCount
= (GdtrDesc
.Limit
+ 1) / sizeof (IA32_SEGMENT_DESCRIPTOR
);
208 GdtEntry
= (IA32_SEGMENT_DESCRIPTOR
*) GdtrDesc
.Base
;
209 for (Index
= 0; Index
< GdtEntryCount
; Index
++) {
210 if (GdtEntry
->Bits
.L
== 0) {
211 if (GdtEntry
->Bits
.Type
> 8 && GdtEntry
->Bits
.L
== 0) {
217 ASSERT (Index
!= -1);
224 @param[in, out] Buffer Pointer to private data buffer.
232 CPU_MP_DATA
*CpuMpData
;
233 BOOLEAN MwaitSupport
;
234 ASM_RELOCATE_AP_LOOP AsmRelocateApLoopFunc
;
235 UINTN ProcessorNumber
;
237 MpInitLibWhoAmI (&ProcessorNumber
);
238 CpuMpData
= GetCpuMpData ();
239 MwaitSupport
= IsMwaitSupport ();
240 AsmRelocateApLoopFunc
= (ASM_RELOCATE_AP_LOOP
) (UINTN
) mReservedApLoopFunc
;
241 AsmRelocateApLoopFunc (
243 CpuMpData
->ApTargetCState
,
244 CpuMpData
->PmCodeSegment
,
245 mReservedTopOfApStack
- ProcessorNumber
* AP_SAFE_STACK_SIZE
,
246 (UINTN
) &mNumberToFinish
249 // It should never reach here
255 Callback function for ExitBootServices.
257 @param[in] Event Event whose notification function is being invoked.
258 @param[in] Context The pointer to the notification function's context,
259 which is implementation-dependent.
264 MpInitChangeApLoopCallback (
269 CPU_MP_DATA
*CpuMpData
;
271 CpuMpData
= GetCpuMpData ();
272 CpuMpData
->PmCodeSegment
= GetProtectedModeCS ();
273 CpuMpData
->ApLoopMode
= PcdGet8 (PcdCpuApLoopMode
);
274 mNumberToFinish
= CpuMpData
->CpuCount
- 1;
275 WakeUpAP (CpuMpData
, TRUE
, 0, RelocateApLoop
, NULL
);
276 while (mNumberToFinish
> 0) {
279 DEBUG ((DEBUG_INFO
, "%a() done!\n", __FUNCTION__
));
283 Initialize global data for MP support.
285 @param[in] CpuMpData The pointer to CPU MP Data structure.
289 IN CPU_MP_DATA
*CpuMpData
293 EFI_PHYSICAL_ADDRESS Address
;
294 UINTN ApSafeBufferSize
;
296 EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc
;
298 CPU_INFO_IN_HOB
*CpuInfoInHob
;
300 SaveCpuMpData (CpuMpData
);
302 if (CpuMpData
->CpuCount
== 1) {
304 // If only BSP exists, return
309 if (PcdGetBool (PcdCpuStackGuard
)) {
311 // One extra page at the bottom of the stack is needed for Guard page.
313 if (CpuMpData
->CpuApStackSize
<= EFI_PAGE_SIZE
) {
314 DEBUG ((DEBUG_ERROR
, "PcdCpuApStackSize is not big enough for Stack Guard!\n"));
319 // DXE will reuse stack allocated for APs at PEI phase if it's available.
320 // Let's check it here.
322 // Note: BSP's stack guard is set at DxeIpl phase. But for the sake of
323 // BSP/AP exchange, stack guard for ApTopOfStack of cpu 0 will still be
326 CpuInfoInHob
= (CPU_INFO_IN_HOB
*)(UINTN
)CpuMpData
->CpuInfoInHob
;
327 for (Index
= 0; Index
< CpuMpData
->CpuCount
; ++Index
) {
328 if (CpuInfoInHob
!= NULL
&& CpuInfoInHob
[Index
].ApTopOfStack
!= 0) {
329 StackBase
= CpuInfoInHob
[Index
].ApTopOfStack
- CpuMpData
->CpuApStackSize
;
331 StackBase
= CpuMpData
->Buffer
+ Index
* CpuMpData
->CpuApStackSize
;
334 Status
= gDS
->GetMemorySpaceDescriptor (StackBase
, &MemDesc
);
335 ASSERT_EFI_ERROR (Status
);
337 Status
= gDS
->SetMemorySpaceAttributes (
339 EFI_PAGES_TO_SIZE (1),
340 MemDesc
.Attributes
| EFI_MEMORY_RP
342 ASSERT_EFI_ERROR (Status
);
344 DEBUG ((DEBUG_INFO
, "Stack Guard set at %lx [cpu%lu]!\n",
345 (UINT64
)StackBase
, (UINT64
)Index
));
350 // Avoid APs access invalid buffer data which allocated by BootServices,
351 // so we will allocate reserved data for AP loop code. We also need to
352 // allocate this buffer below 4GB due to APs may be transferred to 32bit
353 // protected mode on long mode DXE.
354 // Allocating it in advance since memory services are not available in
355 // Exit Boot Services callback function.
357 ApSafeBufferSize
= CpuMpData
->AddressMap
.RelocateApLoopFuncSize
;
358 ApSafeBufferSize
+= CpuMpData
->CpuCount
* AP_SAFE_STACK_SIZE
;
360 Address
= BASE_4GB
- 1;
361 Status
= gBS
->AllocatePages (
363 EfiReservedMemoryType
,
364 EFI_SIZE_TO_PAGES (ApSafeBufferSize
),
367 ASSERT_EFI_ERROR (Status
);
368 mReservedApLoopFunc
= (VOID
*) (UINTN
) Address
;
369 ASSERT (mReservedApLoopFunc
!= NULL
);
370 mReservedTopOfApStack
= (UINTN
) Address
+ EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (ApSafeBufferSize
));
371 ASSERT ((mReservedTopOfApStack
& (UINTN
)(CPU_STACK_ALIGNMENT
- 1)) == 0);
374 CpuMpData
->AddressMap
.RelocateApLoopFuncAddress
,
375 CpuMpData
->AddressMap
.RelocateApLoopFuncSize
378 Status
= gBS
->CreateEvent (
379 EVT_TIMER
| EVT_NOTIFY_SIGNAL
,
385 ASSERT_EFI_ERROR (Status
);
388 // Set timer to check all APs status.
390 Status
= gBS
->SetTimer (
395 ASSERT_EFI_ERROR (Status
);
397 Status
= gBS
->CreateEvent (
398 EVT_SIGNAL_EXIT_BOOT_SERVICES
,
400 MpInitChangeApLoopCallback
,
402 &mMpInitExitBootServicesEvent
404 ASSERT_EFI_ERROR (Status
);
406 Status
= gBS
->CreateEventEx (
409 MpInitChangeApLoopCallback
,
411 &gEfiEventLegacyBootGuid
,
414 ASSERT_EFI_ERROR (Status
);
418 This service executes a caller provided function on all enabled APs.
420 @param[in] Procedure A pointer to the function to be run on
421 enabled APs of the system. See type
423 @param[in] SingleThread If TRUE, then all the enabled APs execute
424 the function specified by Procedure one by
425 one, in ascending order of processor handle
426 number. If FALSE, then all the enabled APs
427 execute the function specified by Procedure
429 @param[in] WaitEvent The event created by the caller with CreateEvent()
430 service. If it is NULL, then execute in
431 blocking mode. BSP waits until all APs finish
432 or TimeoutInMicroSeconds expires. If it's
433 not NULL, then execute in non-blocking mode.
434 BSP requests the function specified by
435 Procedure to be started on all the enabled
436 APs, and go on executing immediately. If
437 all return from Procedure, or TimeoutInMicroSeconds
438 expires, this event is signaled. The BSP
439 can use the CheckEvent() or WaitForEvent()
440 services to check the state of event. Type
441 EFI_EVENT is defined in CreateEvent() in
442 the Unified Extensible Firmware Interface
444 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
445 APs to return from Procedure, either for
446 blocking or non-blocking mode. Zero means
447 infinity. If the timeout expires before
448 all APs return from Procedure, then Procedure
449 on the failed APs is terminated. All enabled
450 APs are available for next function assigned
451 by MpInitLibStartupAllAPs() or
452 MPInitLibStartupThisAP().
453 If the timeout expires in blocking mode,
454 BSP returns EFI_TIMEOUT. If the timeout
455 expires in non-blocking mode, WaitEvent
456 is signaled with SignalEvent().
457 @param[in] ProcedureArgument The parameter passed into Procedure for
459 @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise,
460 if all APs finish successfully, then its
461 content is set to NULL. If not all APs
462 finish before timeout expires, then its
463 content is set to address of the buffer
464 holding handle numbers of the failed APs.
465 The buffer is allocated by MP Initialization
466 library, and it's the caller's responsibility to
467 free the buffer with FreePool() service.
468 In blocking mode, it is ready for consumption
469 when the call returns. In non-blocking mode,
470 it is ready when WaitEvent is signaled. The
471 list of failed CPU is terminated by
474 @retval EFI_SUCCESS In blocking mode, all APs have finished before
476 @retval EFI_SUCCESS In non-blocking mode, function has been dispatched
478 @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
479 UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
481 @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not
483 @retval EFI_DEVICE_ERROR Caller processor is AP.
484 @retval EFI_NOT_STARTED No enabled APs exist in the system.
485 @retval EFI_NOT_READY Any enabled APs are busy.
486 @retval EFI_NOT_READY MP Initialize Library is not initialized.
487 @retval EFI_TIMEOUT In blocking mode, the timeout expired before
488 all enabled APs have finished.
489 @retval EFI_INVALID_PARAMETER Procedure is NULL.
494 MpInitLibStartupAllAPs (
495 IN EFI_AP_PROCEDURE Procedure
,
496 IN BOOLEAN SingleThread
,
497 IN EFI_EVENT WaitEvent OPTIONAL
,
498 IN UINTN TimeoutInMicroseconds
,
499 IN VOID
*ProcedureArgument OPTIONAL
,
500 OUT UINTN
**FailedCpuList OPTIONAL
506 // Temporarily stop checkAllApsStatus for avoid resource dead-lock.
508 mStopCheckAllApsStatus
= TRUE
;
510 Status
= StartupAllAPsWorker (
514 TimeoutInMicroseconds
,
520 // Start checkAllApsStatus
522 mStopCheckAllApsStatus
= FALSE
;
528 This service lets the caller get one enabled AP to execute a caller-provided
531 @param[in] Procedure A pointer to the function to be run on the
532 designated AP of the system. See type
534 @param[in] ProcessorNumber The handle number of the AP. The range is
535 from 0 to the total number of logical
536 processors minus 1. The total number of
537 logical processors can be retrieved by
538 MpInitLibGetNumberOfProcessors().
539 @param[in] WaitEvent The event created by the caller with CreateEvent()
540 service. If it is NULL, then execute in
541 blocking mode. BSP waits until this AP finish
542 or TimeoutInMicroSeconds expires. If it's
543 not NULL, then execute in non-blocking mode.
544 BSP requests the function specified by
545 Procedure to be started on this AP,
546 and go on executing immediately. If this AP
547 return from Procedure or TimeoutInMicroSeconds
548 expires, this event is signaled. The BSP
549 can use the CheckEvent() or WaitForEvent()
550 services to check the state of event. Type
551 EFI_EVENT is defined in CreateEvent() in
552 the Unified Extensible Firmware Interface
554 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
555 this AP to finish this Procedure, either for
556 blocking or non-blocking mode. Zero means
557 infinity. If the timeout expires before
558 this AP returns from Procedure, then Procedure
559 on the AP is terminated. The
560 AP is available for next function assigned
561 by MpInitLibStartupAllAPs() or
562 MpInitLibStartupThisAP().
563 If the timeout expires in blocking mode,
564 BSP returns EFI_TIMEOUT. If the timeout
565 expires in non-blocking mode, WaitEvent
566 is signaled with SignalEvent().
567 @param[in] ProcedureArgument The parameter passed into Procedure on the
569 @param[out] Finished If NULL, this parameter is ignored. In
570 blocking mode, this parameter is ignored.
571 In non-blocking mode, if AP returns from
572 Procedure before the timeout expires, its
573 content is set to TRUE. Otherwise, the
574 value is set to FALSE. The caller can
575 determine if the AP returned from Procedure
576 by evaluating this value.
578 @retval EFI_SUCCESS In blocking mode, specified AP finished before
580 @retval EFI_SUCCESS In non-blocking mode, the function has been
581 dispatched to specified AP.
582 @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
583 UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
585 @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not
587 @retval EFI_DEVICE_ERROR The calling processor is an AP.
588 @retval EFI_TIMEOUT In blocking mode, the timeout expired before
589 the specified AP has finished.
590 @retval EFI_NOT_READY The specified AP is busy.
591 @retval EFI_NOT_READY MP Initialize Library is not initialized.
592 @retval EFI_NOT_FOUND The processor with the handle specified by
593 ProcessorNumber does not exist.
594 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.
595 @retval EFI_INVALID_PARAMETER Procedure is NULL.
600 MpInitLibStartupThisAP (
601 IN EFI_AP_PROCEDURE Procedure
,
602 IN UINTN ProcessorNumber
,
603 IN EFI_EVENT WaitEvent OPTIONAL
,
604 IN UINTN TimeoutInMicroseconds
,
605 IN VOID
*ProcedureArgument OPTIONAL
,
606 OUT BOOLEAN
*Finished OPTIONAL
612 // temporarily stop checkAllApsStatus for avoid resource dead-lock.
614 mStopCheckAllApsStatus
= TRUE
;
616 Status
= StartupThisAPWorker (
620 TimeoutInMicroseconds
,
625 mStopCheckAllApsStatus
= FALSE
;
631 This service switches the requested AP to be the BSP from that point onward.
632 This service changes the BSP for all purposes. This call can only be performed
635 @param[in] ProcessorNumber The handle number of AP that is to become the new
636 BSP. The range is from 0 to the total number of
637 logical processors minus 1. The total number of
638 logical processors can be retrieved by
639 MpInitLibGetNumberOfProcessors().
640 @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an
641 enabled AP. Otherwise, it will be disabled.
643 @retval EFI_SUCCESS BSP successfully switched.
644 @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to
645 this service returning.
646 @retval EFI_UNSUPPORTED Switching the BSP is not supported.
647 @retval EFI_DEVICE_ERROR The calling processor is an AP.
648 @retval EFI_NOT_FOUND The processor with the handle specified by
649 ProcessorNumber does not exist.
650 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or
652 @retval EFI_NOT_READY The specified AP is busy.
653 @retval EFI_NOT_READY MP Initialize Library is not initialized.
659 IN UINTN ProcessorNumber
,
660 IN BOOLEAN EnableOldBSP
664 EFI_TIMER_ARCH_PROTOCOL
*Timer
;
669 // Locate Timer Arch Protocol
671 Status
= gBS
->LocateProtocol (&gEfiTimerArchProtocolGuid
, NULL
, (VOID
**) &Timer
);
672 if (EFI_ERROR (Status
)) {
678 // Save current rate of DXE Timer
680 Timer
->GetTimerPeriod (Timer
, &TimerPeriod
);
682 // Disable DXE Timer and drain pending interrupts
684 Timer
->SetTimerPeriod (Timer
, 0);
687 Status
= SwitchBSPWorker (ProcessorNumber
, EnableOldBSP
);
691 // Enable and restore rate of DXE Timer
693 Timer
->SetTimerPeriod (Timer
, TimerPeriod
);
700 This service lets the caller enable or disable an AP from this point onward.
701 This service may only be called from the BSP.
703 @param[in] ProcessorNumber The handle number of AP.
704 The range is from 0 to the total number of
705 logical processors minus 1. The total number of
706 logical processors can be retrieved by
707 MpInitLibGetNumberOfProcessors().
708 @param[in] EnableAP Specifies the new state for the processor for
709 enabled, FALSE for disabled.
710 @param[in] HealthFlag If not NULL, a pointer to a value that specifies
711 the new health status of the AP. This flag
712 corresponds to StatusFlag defined in
713 EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
714 the PROCESSOR_HEALTH_STATUS_BIT is used. All other
715 bits are ignored. If it is NULL, this parameter
718 @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.
719 @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed
720 prior to this service returning.
721 @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported.
722 @retval EFI_DEVICE_ERROR The calling processor is an AP.
723 @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber
725 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP.
726 @retval EFI_NOT_READY MP Initialize Library is not initialized.
731 MpInitLibEnableDisableAP (
732 IN UINTN ProcessorNumber
,
734 IN UINT32
*HealthFlag OPTIONAL
738 BOOLEAN TempStopCheckState
;
740 TempStopCheckState
= FALSE
;
742 // temporarily stop checkAllAPsStatus for initialize parameters.
744 if (!mStopCheckAllApsStatus
) {
745 mStopCheckAllApsStatus
= TRUE
;
746 TempStopCheckState
= TRUE
;
749 Status
= EnableDisableApWorker (ProcessorNumber
, EnableAP
, HealthFlag
);
751 if (TempStopCheckState
) {
752 mStopCheckAllApsStatus
= FALSE
;