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
,
38 EFI_PEI_NOTIFY_DESCRIPTOR mS3SmmInitDoneNotifyDesc
= {
39 EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
,
40 &gEdkiiS3SmmInitDoneGuid
,
41 NotifyOnS3SmmInitDonePpi
45 S3 SMM Init Done notification function.
47 @param PeiServices Indirect reference to the PEI Services Table.
48 @param NotifyDesc Address of the notification descriptor data structure.
49 @param InvokePpi Address of the PPI that was invoked.
51 @retval EFI_SUCCESS The function completes successfully.
56 NotifyOnS3SmmInitDonePpi (
57 IN EFI_PEI_SERVICES
**PeiServices
,
58 IN EFI_PEI_NOTIFY_DESCRIPTOR
*NotifyDesc
,
62 CPU_MP_DATA
*CpuMpData
;
64 CpuMpData
= GetCpuMpData ();
67 // PiSmmCpuDxeSmm driver hardcode change the loop mode to HLT mode.
68 // So in this notify function, code need to check the current loop
69 // mode, if it is not HLT mode, code need to change loop mode back
70 // to the original mode.
72 if (CpuMpData
->ApLoopMode
!= ApInHltLoop
) {
73 CpuMpData
->WakeUpByInitSipiSipi
= TRUE
;
81 Enable Debug Agent to support source debugging on AP function.
92 Get pointer to CPU MP Data structure.
93 For BSP, the pointer is retrieved from HOB.
94 For AP, the structure is just after IDT.
96 @return The pointer to CPU MP Data structure.
103 CPU_MP_DATA
*CpuMpData
;
104 MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr
;
105 IA32_DESCRIPTOR Idtr
;
107 ApicBaseMsr
.Uint64
= AsmReadMsr64 (MSR_IA32_APIC_BASE
);
108 if (ApicBaseMsr
.Bits
.BSP
== 1) {
109 CpuMpData
= GetCpuMpDataFromGuidedHob ();
110 ASSERT (CpuMpData
!= NULL
);
113 CpuMpData
= (CPU_MP_DATA
*) (Idtr
.Base
+ Idtr
.Limit
+ 1);
119 Save the pointer to CPU MP Data structure.
121 @param[in] CpuMpData The pointer to CPU MP Data structure will be saved.
125 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
))) {
179 Hob
.Raw
= GET_NEXT_HOB (Hob
);
185 Get available system memory below 1MB by specified size.
187 @param[in] WakeupBufferSize Wakeup buffer size required
189 @retval other Return wakeup buffer address below 1MB.
190 @retval -1 Cannot find free memory below 1MB.
194 IN UINTN WakeupBufferSize
197 EFI_PEI_HOB_POINTERS Hob
;
198 UINT64 WakeupBufferStart
;
199 UINT64 WakeupBufferEnd
;
201 WakeupBufferSize
= (WakeupBufferSize
+ SIZE_4KB
- 1) & ~(SIZE_4KB
- 1);
204 // Get the HOB list for processing
206 Hob
.Raw
= GetHobList ();
209 // Collect memory ranges
211 while (!END_OF_HOB_LIST (Hob
)) {
212 if (Hob
.Header
->HobType
== EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
) {
213 if ((Hob
.ResourceDescriptor
->PhysicalStart
< BASE_1MB
) &&
214 (Hob
.ResourceDescriptor
->ResourceType
== EFI_RESOURCE_SYSTEM_MEMORY
) &&
215 ((Hob
.ResourceDescriptor
->ResourceAttribute
&
216 (EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED
|
217 EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED
|
218 EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED
222 // Need memory under 1MB to be collected here
224 WakeupBufferEnd
= Hob
.ResourceDescriptor
->PhysicalStart
+ Hob
.ResourceDescriptor
->ResourceLength
;
225 if (PcdGetBool (PcdSevEsIsEnabled
) &&
226 WakeupBufferEnd
> mSevEsPeiWakeupBuffer
) {
228 // SEV-ES Wakeup buffer should be under 1MB and under any previous one
230 WakeupBufferEnd
= mSevEsPeiWakeupBuffer
;
231 } else if (WakeupBufferEnd
> BASE_1MB
) {
233 // Wakeup buffer should be under 1MB
235 WakeupBufferEnd
= BASE_1MB
;
237 while (WakeupBufferEnd
> WakeupBufferSize
) {
239 // Wakeup buffer should be aligned on 4KB
241 WakeupBufferStart
= (WakeupBufferEnd
- WakeupBufferSize
) & ~(SIZE_4KB
- 1);
242 if (WakeupBufferStart
< Hob
.ResourceDescriptor
->PhysicalStart
) {
245 if (CheckOverlapWithAllocatedBuffer (WakeupBufferStart
, WakeupBufferEnd
)) {
247 // If this range is overlapped with existing allocated buffer, skip it
248 // and find the next range
250 WakeupBufferEnd
-= WakeupBufferSize
;
253 DEBUG ((DEBUG_INFO
, "WakeupBufferStart = %x, WakeupBufferSize = %x\n",
254 WakeupBufferStart
, WakeupBufferSize
));
256 if (PcdGetBool (PcdSevEsIsEnabled
)) {
258 // Next SEV-ES wakeup buffer allocation must be below this
261 mSevEsPeiWakeupBuffer
= WakeupBufferStart
;
264 return (UINTN
)WakeupBufferStart
;
271 Hob
.Raw
= GET_NEXT_HOB (Hob
);
278 Get available EfiBootServicesCode memory below 4GB by specified size.
280 This buffer is required to safely transfer AP from real address mode to
281 protected mode or long mode, due to the fact that the buffer returned by
282 GetWakeupBuffer() may be marked as non-executable.
284 @param[in] BufferSize Wakeup transition buffer size.
286 @retval other Return wakeup transition buffer address below 4GB.
287 @retval 0 Cannot find free memory below 4GB.
290 GetModeTransitionBuffer (
295 // PEI phase doesn't need to do such transition. So simply return 0.
301 Return the address of the SEV-ES AP jump table.
303 This buffer is required in order for an SEV-ES guest to transition from
306 @return Return SEV-ES AP jump table buffer
314 // PEI phase doesn't need to do such transition. So simply return 0.
320 Checks APs status and updates APs status if needed.
324 CheckAndUpdateApsStatus (
331 Build the microcode patch HOB that contains the base address and size of the
332 microcode patch stored in the memory.
334 @param[in] CpuMpData Pointer to the CPU_MP_DATA structure.
338 BuildMicrocodeCacheHob (
339 IN CPU_MP_DATA
*CpuMpData
342 EDKII_MICROCODE_PATCH_HOB
*MicrocodeHob
;
346 HobDataLength
= sizeof (EDKII_MICROCODE_PATCH_HOB
) +
347 sizeof (UINT64
) * CpuMpData
->CpuCount
;
349 MicrocodeHob
= AllocatePool (HobDataLength
);
350 if (MicrocodeHob
== NULL
) {
356 // Store the information of the memory region that holds the microcode patches.
358 MicrocodeHob
->MicrocodePatchAddress
= CpuMpData
->MicrocodePatchAddress
;
359 MicrocodeHob
->MicrocodePatchRegionSize
= CpuMpData
->MicrocodePatchRegionSize
;
362 // Store the detected microcode patch for each processor as well.
364 MicrocodeHob
->ProcessorCount
= CpuMpData
->CpuCount
;
365 for (Index
= 0; Index
< CpuMpData
->CpuCount
; Index
++) {
366 if (CpuMpData
->CpuData
[Index
].MicrocodeEntryAddr
!= 0) {
367 MicrocodeHob
->ProcessorSpecificPatchOffset
[Index
] =
368 CpuMpData
->CpuData
[Index
].MicrocodeEntryAddr
- CpuMpData
->MicrocodePatchAddress
;
370 MicrocodeHob
->ProcessorSpecificPatchOffset
[Index
] = MAX_UINT64
;
375 &gEdkiiMicrocodePatchHobGuid
,
384 Initialize global data for MP support.
386 @param[in] CpuMpData The pointer to CPU MP Data structure.
390 IN CPU_MP_DATA
*CpuMpData
395 BuildMicrocodeCacheHob (CpuMpData
);
396 SaveCpuMpData (CpuMpData
);
401 Status
= PeiServicesNotifyPpi (&mS3SmmInitDoneNotifyDesc
);
402 ASSERT_EFI_ERROR (Status
);
406 This service executes a caller provided function on all enabled APs.
408 @param[in] Procedure A pointer to the function to be run on
409 enabled APs of the system. See type
411 @param[in] SingleThread If TRUE, then all the enabled APs execute
412 the function specified by Procedure one by
413 one, in ascending order of processor handle
414 number. If FALSE, then all the enabled APs
415 execute the function specified by Procedure
417 @param[in] WaitEvent The event created by the caller with CreateEvent()
418 service. If it is NULL, then execute in
419 blocking mode. BSP waits until all APs finish
420 or TimeoutInMicroSeconds expires. If it's
421 not NULL, then execute in non-blocking mode.
422 BSP requests the function specified by
423 Procedure to be started on all the enabled
424 APs, and go on executing immediately. If
425 all return from Procedure, or TimeoutInMicroSeconds
426 expires, this event is signaled. The BSP
427 can use the CheckEvent() or WaitForEvent()
428 services to check the state of event. Type
429 EFI_EVENT is defined in CreateEvent() in
430 the Unified Extensible Firmware Interface
432 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
433 APs to return from Procedure, either for
434 blocking or non-blocking mode. Zero means
435 infinity. If the timeout expires before
436 all APs return from Procedure, then Procedure
437 on the failed APs is terminated. All enabled
438 APs are available for next function assigned
439 by MpInitLibStartupAllAPs() or
440 MPInitLibStartupThisAP().
441 If the timeout expires in blocking mode,
442 BSP returns EFI_TIMEOUT. If the timeout
443 expires in non-blocking mode, WaitEvent
444 is signaled with SignalEvent().
445 @param[in] ProcedureArgument The parameter passed into Procedure for
447 @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise,
448 if all APs finish successfully, then its
449 content is set to NULL. If not all APs
450 finish before timeout expires, then its
451 content is set to address of the buffer
452 holding handle numbers of the failed APs.
453 The buffer is allocated by MP Initialization
454 library, and it's the caller's responsibility to
455 free the buffer with FreePool() service.
456 In blocking mode, it is ready for consumption
457 when the call returns. In non-blocking mode,
458 it is ready when WaitEvent is signaled. The
459 list of failed CPU is terminated by
462 @retval EFI_SUCCESS In blocking mode, all APs have finished before
464 @retval EFI_SUCCESS In non-blocking mode, function has been dispatched
466 @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
467 UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
469 @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not
471 @retval EFI_DEVICE_ERROR Caller processor is AP.
472 @retval EFI_NOT_STARTED No enabled APs exist in the system.
473 @retval EFI_NOT_READY Any enabled APs are busy.
474 @retval EFI_NOT_READY MP Initialize Library is not initialized.
475 @retval EFI_TIMEOUT In blocking mode, the timeout expired before
476 all enabled APs have finished.
477 @retval EFI_INVALID_PARAMETER Procedure is NULL.
482 MpInitLibStartupAllAPs (
483 IN EFI_AP_PROCEDURE Procedure
,
484 IN BOOLEAN SingleThread
,
485 IN EFI_EVENT WaitEvent OPTIONAL
,
486 IN UINTN TimeoutInMicroseconds
,
487 IN VOID
*ProcedureArgument OPTIONAL
,
488 OUT UINTN
**FailedCpuList OPTIONAL
491 if (WaitEvent
!= NULL
) {
492 return EFI_UNSUPPORTED
;
495 return StartupAllCPUsWorker (
500 TimeoutInMicroseconds
,
507 This service lets the caller get one enabled AP to execute a caller-provided
510 @param[in] Procedure A pointer to the function to be run on the
511 designated AP of the system. See type
513 @param[in] ProcessorNumber The handle number of the AP. The range is
514 from 0 to the total number of logical
515 processors minus 1. The total number of
516 logical processors can be retrieved by
517 MpInitLibGetNumberOfProcessors().
518 @param[in] WaitEvent The event created by the caller with CreateEvent()
519 service. If it is NULL, then execute in
520 blocking mode. BSP waits until this AP finish
521 or TimeoutInMicroSeconds expires. If it's
522 not NULL, then execute in non-blocking mode.
523 BSP requests the function specified by
524 Procedure to be started on this AP,
525 and go on executing immediately. If this AP
526 return from Procedure or TimeoutInMicroSeconds
527 expires, this event is signaled. The BSP
528 can use the CheckEvent() or WaitForEvent()
529 services to check the state of event. Type
530 EFI_EVENT is defined in CreateEvent() in
531 the Unified Extensible Firmware Interface
533 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
534 this AP to finish this Procedure, either for
535 blocking or non-blocking mode. Zero means
536 infinity. If the timeout expires before
537 this AP returns from Procedure, then Procedure
538 on the AP is terminated. The
539 AP is available for next function assigned
540 by MpInitLibStartupAllAPs() or
541 MpInitLibStartupThisAP().
542 If the timeout expires in blocking mode,
543 BSP returns EFI_TIMEOUT. If the timeout
544 expires in non-blocking mode, WaitEvent
545 is signaled with SignalEvent().
546 @param[in] ProcedureArgument The parameter passed into Procedure on the
548 @param[out] Finished If NULL, this parameter is ignored. In
549 blocking mode, this parameter is ignored.
550 In non-blocking mode, if AP returns from
551 Procedure before the timeout expires, its
552 content is set to TRUE. Otherwise, the
553 value is set to FALSE. The caller can
554 determine if the AP returned from Procedure
555 by evaluating this value.
557 @retval EFI_SUCCESS In blocking mode, specified AP finished before
559 @retval EFI_SUCCESS In non-blocking mode, the function has been
560 dispatched to specified AP.
561 @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
562 UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
564 @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not
566 @retval EFI_DEVICE_ERROR The calling processor is an AP.
567 @retval EFI_TIMEOUT In blocking mode, the timeout expired before
568 the specified AP has finished.
569 @retval EFI_NOT_READY The specified AP is busy.
570 @retval EFI_NOT_READY MP Initialize Library is not initialized.
571 @retval EFI_NOT_FOUND The processor with the handle specified by
572 ProcessorNumber does not exist.
573 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.
574 @retval EFI_INVALID_PARAMETER Procedure is NULL.
579 MpInitLibStartupThisAP (
580 IN EFI_AP_PROCEDURE Procedure
,
581 IN UINTN ProcessorNumber
,
582 IN EFI_EVENT WaitEvent OPTIONAL
,
583 IN UINTN TimeoutInMicroseconds
,
584 IN VOID
*ProcedureArgument OPTIONAL
,
585 OUT BOOLEAN
*Finished OPTIONAL
588 if (WaitEvent
!= NULL
) {
589 return EFI_UNSUPPORTED
;
592 return StartupThisAPWorker (
596 TimeoutInMicroseconds
,
603 This service switches the requested AP to be the BSP from that point onward.
604 This service changes the BSP for all purposes. This call can only be performed
607 @param[in] ProcessorNumber The handle number of AP that is to become the new
608 BSP. The range is from 0 to the total number of
609 logical processors minus 1. The total number of
610 logical processors can be retrieved by
611 MpInitLibGetNumberOfProcessors().
612 @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an
613 enabled AP. Otherwise, it will be disabled.
615 @retval EFI_SUCCESS BSP successfully switched.
616 @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to
617 this service returning.
618 @retval EFI_UNSUPPORTED Switching the BSP is not supported.
619 @retval EFI_DEVICE_ERROR The calling processor is an AP.
620 @retval EFI_NOT_FOUND The processor with the handle specified by
621 ProcessorNumber does not exist.
622 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or
624 @retval EFI_NOT_READY The specified AP is busy.
625 @retval EFI_NOT_READY MP Initialize Library is not initialized.
631 IN UINTN ProcessorNumber
,
632 IN BOOLEAN EnableOldBSP
635 return SwitchBSPWorker (ProcessorNumber
, EnableOldBSP
);
639 This service lets the caller enable or disable an AP from this point onward.
640 This service may only be called from the BSP.
642 @param[in] ProcessorNumber The handle number of AP.
643 The range is from 0 to the total number of
644 logical processors minus 1. The total number of
645 logical processors can be retrieved by
646 MpInitLibGetNumberOfProcessors().
647 @param[in] EnableAP Specifies the new state for the processor for
648 enabled, FALSE for disabled.
649 @param[in] HealthFlag If not NULL, a pointer to a value that specifies
650 the new health status of the AP. This flag
651 corresponds to StatusFlag defined in
652 EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
653 the PROCESSOR_HEALTH_STATUS_BIT is used. All other
654 bits are ignored. If it is NULL, this parameter
657 @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.
658 @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed
659 prior to this service returning.
660 @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported.
661 @retval EFI_DEVICE_ERROR The calling processor is an AP.
662 @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber
664 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP.
665 @retval EFI_NOT_READY MP Initialize Library is not initialized.
670 MpInitLibEnableDisableAP (
671 IN UINTN ProcessorNumber
,
673 IN UINT32
*HealthFlag OPTIONAL
676 return EnableDisableApWorker (ProcessorNumber
, EnableAP
, HealthFlag
);
680 This funtion will try to invoke platform specific microcode shadow logic to
681 relocate microcode update patches into memory.
683 @param[in, out] CpuMpData The pointer to CPU MP Data structure.
685 @retval EFI_SUCCESS Shadow microcode success.
686 @retval EFI_OUT_OF_RESOURCES No enough resource to complete the operation.
687 @retval EFI_UNSUPPORTED Can't find platform specific microcode shadow
691 PlatformShadowMicrocode (
692 IN OUT CPU_MP_DATA
*CpuMpData
696 EDKII_PEI_SHADOW_MICROCODE_PPI
*ShadowMicrocodePpi
;
698 EDKII_PEI_MICROCODE_CPU_ID
*MicrocodeCpuId
;
703 Status
= PeiServicesLocatePpi (
704 &gEdkiiPeiShadowMicrocodePpiGuid
,
707 (VOID
**) &ShadowMicrocodePpi
709 if (EFI_ERROR (Status
)) {
710 return EFI_UNSUPPORTED
;
713 CpuCount
= CpuMpData
->CpuCount
;
714 MicrocodeCpuId
= (EDKII_PEI_MICROCODE_CPU_ID
*) AllocateZeroPool (sizeof (EDKII_PEI_MICROCODE_CPU_ID
) * CpuCount
);
715 if (MicrocodeCpuId
== NULL
) {
716 return EFI_OUT_OF_RESOURCES
;
719 for (Index
= 0; Index
< CpuMpData
->CpuCount
; Index
++) {
720 MicrocodeCpuId
[Index
].ProcessorSignature
= CpuMpData
->CpuData
[Index
].ProcessorSignature
;
721 MicrocodeCpuId
[Index
].PlatformId
= CpuMpData
->CpuData
[Index
].PlatformId
;
724 Status
= ShadowMicrocodePpi
->ShadowMicrocode (
731 FreePool (MicrocodeCpuId
);
732 if (EFI_ERROR (Status
)) {
733 return EFI_NOT_FOUND
;
736 CpuMpData
->MicrocodePatchAddress
= (UINTN
) Buffer
;
737 CpuMpData
->MicrocodePatchRegionSize
= BufferSize
;
741 "%a: Required microcode patches have been loaded at 0x%lx, with size 0x%lx.\n",
742 __FUNCTION__
, CpuMpData
->MicrocodePatchAddress
, CpuMpData
->MicrocodePatchRegionSize