2 MP initialize support functions for PEI phase.
4 Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include <Library/PeiServicesLib.h>
11 #include <Guid/S3SmmInitDone.h>
12 #include <Ppi/ShadowMicrocode.h>
14 STATIC UINT64 mSevEsPeiWakeupBuffer
= BASE_1MB
;
17 S3 SMM Init Done notification function.
19 @param PeiServices Indirect reference to the PEI Services Table.
20 @param NotifyDesc Address of the notification descriptor data structure.
21 @param InvokePpi Address of the PPI that was invoked.
23 @retval EFI_SUCCESS The function completes successfully.
28 NotifyOnS3SmmInitDonePpi (
29 IN EFI_PEI_SERVICES
**PeiServices
,
30 IN EFI_PEI_NOTIFY_DESCRIPTOR
*NotifyDesc
,
37 EFI_PEI_NOTIFY_DESCRIPTOR mS3SmmInitDoneNotifyDesc
= {
38 EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
,
39 &gEdkiiS3SmmInitDoneGuid
,
40 NotifyOnS3SmmInitDonePpi
44 S3 SMM Init Done notification function.
46 @param PeiServices Indirect reference to the PEI Services Table.
47 @param NotifyDesc Address of the notification descriptor data structure.
48 @param InvokePpi Address of the PPI that was invoked.
50 @retval EFI_SUCCESS The function completes successfully.
55 NotifyOnS3SmmInitDonePpi (
56 IN EFI_PEI_SERVICES
**PeiServices
,
57 IN EFI_PEI_NOTIFY_DESCRIPTOR
*NotifyDesc
,
61 CPU_MP_DATA
*CpuMpData
;
63 CpuMpData
= GetCpuMpData ();
66 // PiSmmCpuDxeSmm driver hardcode change the loop mode to HLT mode.
67 // So in this notify function, code need to check the current loop
68 // mode, if it is not HLT mode, code need to change loop mode back
69 // to the original mode.
71 if (CpuMpData
->ApLoopMode
!= ApInHltLoop
) {
72 CpuMpData
->WakeUpByInitSipiSipi
= TRUE
;
79 Enable Debug Agent to support source debugging on AP function.
90 Get pointer to CPU MP Data structure.
91 For BSP, the pointer is retrieved from HOB.
92 For AP, the structure is just after IDT.
94 @return The pointer to CPU MP Data structure.
101 CPU_MP_DATA
*CpuMpData
;
102 MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr
;
103 IA32_DESCRIPTOR Idtr
;
105 ApicBaseMsr
.Uint64
= AsmReadMsr64 (MSR_IA32_APIC_BASE
);
106 if (ApicBaseMsr
.Bits
.BSP
== 1) {
107 CpuMpData
= GetCpuMpDataFromGuidedHob ();
108 ASSERT (CpuMpData
!= NULL
);
111 CpuMpData
= (CPU_MP_DATA
*)(Idtr
.Base
+ Idtr
.Limit
+ 1);
118 Save the pointer to CPU MP Data structure.
120 @param[in] CpuMpData The pointer to CPU MP Data structure will be saved.
124 IN CPU_MP_DATA
*CpuMpData
130 // Build location of CPU MP DATA buffer in HOB
132 Data64
= (UINT64
)(UINTN
)CpuMpData
;
134 &mCpuInitMpLibHobGuid
,
141 Check if AP wakeup buffer is overlapped with existing allocated buffer.
143 @param[in] WakeupBufferStart AP wakeup buffer start address.
144 @param[in] WakeupBufferEnd AP wakeup buffer end address.
146 @retval TRUE There is overlap.
147 @retval FALSE There is no overlap.
150 CheckOverlapWithAllocatedBuffer (
151 IN UINT64 WakeupBufferStart
,
152 IN UINT64 WakeupBufferEnd
155 EFI_PEI_HOB_POINTERS Hob
;
156 EFI_HOB_MEMORY_ALLOCATION
*MemoryHob
;
163 // Get the HOB list for processing
165 Hob
.Raw
= GetHobList ();
167 // Collect memory ranges
169 while (!END_OF_HOB_LIST (Hob
)) {
170 if (Hob
.Header
->HobType
== EFI_HOB_TYPE_MEMORY_ALLOCATION
) {
171 MemoryHob
= Hob
.MemoryAllocation
;
172 MemoryStart
= MemoryHob
->AllocDescriptor
.MemoryBaseAddress
;
173 MemoryEnd
= MemoryHob
->AllocDescriptor
.MemoryBaseAddress
+ MemoryHob
->AllocDescriptor
.MemoryLength
;
174 if (!((WakeupBufferStart
>= MemoryEnd
) || (WakeupBufferEnd
<= MemoryStart
))) {
180 Hob
.Raw
= GET_NEXT_HOB (Hob
);
187 Get available system memory below 1MB by specified size.
189 @param[in] WakeupBufferSize Wakeup buffer size required
191 @retval other Return wakeup buffer address below 1MB.
192 @retval -1 Cannot find free memory below 1MB.
196 IN UINTN WakeupBufferSize
199 EFI_PEI_HOB_POINTERS Hob
;
200 UINT64 WakeupBufferStart
;
201 UINT64 WakeupBufferEnd
;
203 WakeupBufferSize
= (WakeupBufferSize
+ SIZE_4KB
- 1) & ~(SIZE_4KB
- 1);
206 // Get the HOB list for processing
208 Hob
.Raw
= GetHobList ();
211 // Collect memory ranges
213 while (!END_OF_HOB_LIST (Hob
)) {
214 if (Hob
.Header
->HobType
== EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
) {
215 if ((Hob
.ResourceDescriptor
->PhysicalStart
< BASE_1MB
) &&
216 (Hob
.ResourceDescriptor
->ResourceType
== EFI_RESOURCE_SYSTEM_MEMORY
) &&
217 ((Hob
.ResourceDescriptor
->ResourceAttribute
&
218 (EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED
|
219 EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED
|
220 EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED
225 // Need memory under 1MB to be collected here
227 WakeupBufferEnd
= Hob
.ResourceDescriptor
->PhysicalStart
+ Hob
.ResourceDescriptor
->ResourceLength
;
228 if (ConfidentialComputingGuestHas (CCAttrAmdSevEs
) &&
229 (WakeupBufferEnd
> mSevEsPeiWakeupBuffer
))
232 // SEV-ES Wakeup buffer should be under 1MB and under any previous one
234 WakeupBufferEnd
= mSevEsPeiWakeupBuffer
;
235 } else if (WakeupBufferEnd
> BASE_1MB
) {
237 // Wakeup buffer should be under 1MB
239 WakeupBufferEnd
= BASE_1MB
;
242 while (WakeupBufferEnd
> WakeupBufferSize
) {
244 // Wakeup buffer should be aligned on 4KB
246 WakeupBufferStart
= (WakeupBufferEnd
- WakeupBufferSize
) & ~(SIZE_4KB
- 1);
247 if (WakeupBufferStart
< Hob
.ResourceDescriptor
->PhysicalStart
) {
251 if (CheckOverlapWithAllocatedBuffer (WakeupBufferStart
, WakeupBufferEnd
)) {
253 // If this range is overlapped with existing allocated buffer, skip it
254 // and find the next range
256 WakeupBufferEnd
-= WakeupBufferSize
;
262 "WakeupBufferStart = %x, WakeupBufferSize = %x\n",
267 if (ConfidentialComputingGuestHas (CCAttrAmdSevEs
)) {
269 // Next SEV-ES wakeup buffer allocation must be below this
272 mSevEsPeiWakeupBuffer
= WakeupBufferStart
;
275 return (UINTN
)WakeupBufferStart
;
283 Hob
.Raw
= GET_NEXT_HOB (Hob
);
290 Get available EfiBootServicesCode memory below 4GB by specified size.
292 This buffer is required to safely transfer AP from real address mode to
293 protected mode or long mode, due to the fact that the buffer returned by
294 GetWakeupBuffer() may be marked as non-executable.
296 @param[in] BufferSize Wakeup transition buffer size.
298 @retval other Return wakeup transition buffer address below 4GB.
299 @retval 0 Cannot find free memory below 4GB.
302 GetModeTransitionBuffer (
307 // PEI phase doesn't need to do such transition. So simply return 0.
313 Return the address of the SEV-ES AP jump table.
315 This buffer is required in order for an SEV-ES guest to transition from
318 @return Return SEV-ES AP jump table buffer
326 // PEI phase doesn't need to do such transition. So simply return 0.
332 Checks APs status and updates APs status if needed.
336 CheckAndUpdateApsStatus (
343 Build the microcode patch HOB that contains the base address and size of the
344 microcode patch stored in the memory.
346 @param[in] CpuMpData Pointer to the CPU_MP_DATA structure.
350 BuildMicrocodeCacheHob (
351 IN CPU_MP_DATA
*CpuMpData
354 EDKII_MICROCODE_PATCH_HOB
*MicrocodeHob
;
358 HobDataLength
= sizeof (EDKII_MICROCODE_PATCH_HOB
) +
359 sizeof (UINT64
) * CpuMpData
->CpuCount
;
361 MicrocodeHob
= AllocatePool (HobDataLength
);
362 if (MicrocodeHob
== NULL
) {
368 // Store the information of the memory region that holds the microcode patches.
370 MicrocodeHob
->MicrocodePatchAddress
= CpuMpData
->MicrocodePatchAddress
;
371 MicrocodeHob
->MicrocodePatchRegionSize
= CpuMpData
->MicrocodePatchRegionSize
;
374 // Store the detected microcode patch for each processor as well.
376 MicrocodeHob
->ProcessorCount
= CpuMpData
->CpuCount
;
377 for (Index
= 0; Index
< CpuMpData
->CpuCount
; Index
++) {
378 if (CpuMpData
->CpuData
[Index
].MicrocodeEntryAddr
!= 0) {
379 MicrocodeHob
->ProcessorSpecificPatchOffset
[Index
] =
380 CpuMpData
->CpuData
[Index
].MicrocodeEntryAddr
- CpuMpData
->MicrocodePatchAddress
;
382 MicrocodeHob
->ProcessorSpecificPatchOffset
[Index
] = MAX_UINT64
;
387 &gEdkiiMicrocodePatchHobGuid
,
396 Initialize global data for MP support.
398 @param[in] CpuMpData The pointer to CPU MP Data structure.
402 IN CPU_MP_DATA
*CpuMpData
407 BuildMicrocodeCacheHob (CpuMpData
);
408 SaveCpuMpData (CpuMpData
);
413 Status
= PeiServicesNotifyPpi (&mS3SmmInitDoneNotifyDesc
);
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
503 if (WaitEvent
!= NULL
) {
504 return EFI_UNSUPPORTED
;
507 return StartupAllCPUsWorker (
512 TimeoutInMicroseconds
,
519 This service lets the caller get one enabled AP to execute a caller-provided
522 @param[in] Procedure A pointer to the function to be run on the
523 designated AP of the system. See type
525 @param[in] ProcessorNumber The handle number of the AP. The range is
526 from 0 to the total number of logical
527 processors minus 1. The total number of
528 logical processors can be retrieved by
529 MpInitLibGetNumberOfProcessors().
530 @param[in] WaitEvent The event created by the caller with CreateEvent()
531 service. If it is NULL, then execute in
532 blocking mode. BSP waits until this AP finish
533 or TimeoutInMicroSeconds expires. If it's
534 not NULL, then execute in non-blocking mode.
535 BSP requests the function specified by
536 Procedure to be started on this AP,
537 and go on executing immediately. If this AP
538 return from Procedure or TimeoutInMicroSeconds
539 expires, this event is signaled. The BSP
540 can use the CheckEvent() or WaitForEvent()
541 services to check the state of event. Type
542 EFI_EVENT is defined in CreateEvent() in
543 the Unified Extensible Firmware Interface
545 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
546 this AP to finish this Procedure, either for
547 blocking or non-blocking mode. Zero means
548 infinity. If the timeout expires before
549 this AP returns from Procedure, then Procedure
550 on the AP is terminated. The
551 AP is available for next function assigned
552 by MpInitLibStartupAllAPs() or
553 MpInitLibStartupThisAP().
554 If the timeout expires in blocking mode,
555 BSP returns EFI_TIMEOUT. If the timeout
556 expires in non-blocking mode, WaitEvent
557 is signaled with SignalEvent().
558 @param[in] ProcedureArgument The parameter passed into Procedure on the
560 @param[out] Finished If NULL, this parameter is ignored. In
561 blocking mode, this parameter is ignored.
562 In non-blocking mode, if AP returns from
563 Procedure before the timeout expires, its
564 content is set to TRUE. Otherwise, the
565 value is set to FALSE. The caller can
566 determine if the AP returned from Procedure
567 by evaluating this value.
569 @retval EFI_SUCCESS In blocking mode, specified AP finished before
571 @retval EFI_SUCCESS In non-blocking mode, the function has been
572 dispatched to specified AP.
573 @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
574 UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
576 @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not
578 @retval EFI_DEVICE_ERROR The calling processor is an AP.
579 @retval EFI_TIMEOUT In blocking mode, the timeout expired before
580 the specified AP has finished.
581 @retval EFI_NOT_READY The specified AP is busy.
582 @retval EFI_NOT_READY MP Initialize Library is not initialized.
583 @retval EFI_NOT_FOUND The processor with the handle specified by
584 ProcessorNumber does not exist.
585 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.
586 @retval EFI_INVALID_PARAMETER Procedure is NULL.
591 MpInitLibStartupThisAP (
592 IN EFI_AP_PROCEDURE Procedure
,
593 IN UINTN ProcessorNumber
,
594 IN EFI_EVENT WaitEvent OPTIONAL
,
595 IN UINTN TimeoutInMicroseconds
,
596 IN VOID
*ProcedureArgument OPTIONAL
,
597 OUT BOOLEAN
*Finished OPTIONAL
600 if (WaitEvent
!= NULL
) {
601 return EFI_UNSUPPORTED
;
604 return StartupThisAPWorker (
608 TimeoutInMicroseconds
,
615 This service switches the requested AP to be the BSP from that point onward.
616 This service changes the BSP for all purposes. This call can only be performed
619 @param[in] ProcessorNumber The handle number of AP that is to become the new
620 BSP. The range is from 0 to the total number of
621 logical processors minus 1. The total number of
622 logical processors can be retrieved by
623 MpInitLibGetNumberOfProcessors().
624 @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an
625 enabled AP. Otherwise, it will be disabled.
627 @retval EFI_SUCCESS BSP successfully switched.
628 @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to
629 this service returning.
630 @retval EFI_UNSUPPORTED Switching the BSP is not supported.
631 @retval EFI_DEVICE_ERROR The calling processor is an AP.
632 @retval EFI_NOT_FOUND The processor with the handle specified by
633 ProcessorNumber does not exist.
634 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or
636 @retval EFI_NOT_READY The specified AP is busy.
637 @retval EFI_NOT_READY MP Initialize Library is not initialized.
643 IN UINTN ProcessorNumber
,
644 IN BOOLEAN EnableOldBSP
647 return SwitchBSPWorker (ProcessorNumber
, EnableOldBSP
);
651 This service lets the caller enable or disable an AP from this point onward.
652 This service may only be called from the BSP.
654 @param[in] ProcessorNumber The handle number of AP.
655 The range is from 0 to the total number of
656 logical processors minus 1. The total number of
657 logical processors can be retrieved by
658 MpInitLibGetNumberOfProcessors().
659 @param[in] EnableAP Specifies the new state for the processor for
660 enabled, FALSE for disabled.
661 @param[in] HealthFlag If not NULL, a pointer to a value that specifies
662 the new health status of the AP. This flag
663 corresponds to StatusFlag defined in
664 EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
665 the PROCESSOR_HEALTH_STATUS_BIT is used. All other
666 bits are ignored. If it is NULL, this parameter
669 @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.
670 @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed
671 prior to this service returning.
672 @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported.
673 @retval EFI_DEVICE_ERROR The calling processor is an AP.
674 @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber
676 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP.
677 @retval EFI_NOT_READY MP Initialize Library is not initialized.
682 MpInitLibEnableDisableAP (
683 IN UINTN ProcessorNumber
,
685 IN UINT32
*HealthFlag OPTIONAL
688 return EnableDisableApWorker (ProcessorNumber
, EnableAP
, HealthFlag
);
692 This funtion will try to invoke platform specific microcode shadow logic to
693 relocate microcode update patches into memory.
695 @param[in, out] CpuMpData The pointer to CPU MP Data structure.
697 @retval EFI_SUCCESS Shadow microcode success.
698 @retval EFI_OUT_OF_RESOURCES No enough resource to complete the operation.
699 @retval EFI_UNSUPPORTED Can't find platform specific microcode shadow
703 PlatformShadowMicrocode (
704 IN OUT CPU_MP_DATA
*CpuMpData
708 EDKII_PEI_SHADOW_MICROCODE_PPI
*ShadowMicrocodePpi
;
710 EDKII_PEI_MICROCODE_CPU_ID
*MicrocodeCpuId
;
715 Status
= PeiServicesLocatePpi (
716 &gEdkiiPeiShadowMicrocodePpiGuid
,
719 (VOID
**)&ShadowMicrocodePpi
721 if (EFI_ERROR (Status
)) {
722 return EFI_UNSUPPORTED
;
725 CpuCount
= CpuMpData
->CpuCount
;
726 MicrocodeCpuId
= (EDKII_PEI_MICROCODE_CPU_ID
*)AllocateZeroPool (sizeof (EDKII_PEI_MICROCODE_CPU_ID
) * CpuCount
);
727 if (MicrocodeCpuId
== NULL
) {
728 return EFI_OUT_OF_RESOURCES
;
731 for (Index
= 0; Index
< CpuMpData
->CpuCount
; Index
++) {
732 MicrocodeCpuId
[Index
].ProcessorSignature
= CpuMpData
->CpuData
[Index
].ProcessorSignature
;
733 MicrocodeCpuId
[Index
].PlatformId
= CpuMpData
->CpuData
[Index
].PlatformId
;
736 Status
= ShadowMicrocodePpi
->ShadowMicrocode (
743 FreePool (MicrocodeCpuId
);
744 if (EFI_ERROR (Status
)) {
745 return EFI_NOT_FOUND
;
748 CpuMpData
->MicrocodePatchAddress
= (UINTN
)Buffer
;
749 CpuMpData
->MicrocodePatchRegionSize
= BufferSize
;
753 "%a: Required microcode patches have been loaded at 0x%lx, with size 0x%lx.\n",
755 CpuMpData
->MicrocodePatchAddress
,
756 CpuMpData
->MicrocodePatchRegionSize