2 CPU PEI Module installs CPU Multiple Processor PPI.
4 Copyright (c) 2015, 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.
18 // Global Descriptor Table (GDT)
20 GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT mGdtEntries
[] = {
21 /* selector { Global Segment Descriptor } */
22 /* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //null descriptor
23 /* 0x08 */ {{0xffff, 0, 0, 0x2, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear data segment descriptor
24 /* 0x10 */ {{0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear code segment descriptor
25 /* 0x18 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
26 /* 0x20 */ {{0xffff, 0, 0, 0xa, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system code segment descriptor
27 /* 0x28 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
28 /* 0x30 */ {{0xffff, 0, 0, 0x2, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
29 /* 0x38 */ {{0xffff, 0, 0, 0xa, 1, 0, 1, 0xf, 0, 1, 0, 1, 0}}, //system code segment descriptor
30 /* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
36 GLOBAL_REMOVE_IF_UNREFERENCED IA32_DESCRIPTOR mGdt
= {
37 sizeof (mGdtEntries
) - 1,
41 GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_NOTIFY_DESCRIPTOR mNotifyList
= {
42 (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
43 &gEfiEndOfPeiSignalPpiGuid
,
48 Sort the APIC ID of all processors.
50 This function sorts the APIC ID of all processors so that processor number is
51 assigned in the ascending order of APIC ID which eases MP debugging.
53 @param PeiCpuMpData Pointer to PEI CPU MP Data
57 IN PEI_CPU_MP_DATA
*PeiCpuMpData
67 ApCount
= PeiCpuMpData
->CpuCount
- 1;
70 for (Index1
= 0; Index1
< ApCount
; Index1
++) {
73 // Sort key is the hardware default APIC ID
75 ApicId
= PeiCpuMpData
->CpuData
[Index1
].ApicId
;
76 for (Index2
= Index1
+ 1; Index2
<= ApCount
; Index2
++) {
77 if (ApicId
> PeiCpuMpData
->CpuData
[Index2
].ApicId
) {
79 ApicId
= PeiCpuMpData
->CpuData
[Index2
].ApicId
;
82 if (Index3
!= Index1
) {
83 CopyMem (&CpuData
, &PeiCpuMpData
->CpuData
[Index3
], sizeof (PEI_CPU_DATA
));
85 &PeiCpuMpData
->CpuData
[Index3
],
86 &PeiCpuMpData
->CpuData
[Index1
],
89 CopyMem (&PeiCpuMpData
->CpuData
[Index1
], &CpuData
, sizeof (PEI_CPU_DATA
));
94 // Get the processor number for the BSP
96 ApicId
= GetInitialApicId ();
97 for (Index1
= 0; Index1
< PeiCpuMpData
->CpuCount
; Index1
++) {
98 if (PeiCpuMpData
->CpuData
[Index1
].ApicId
== ApicId
) {
99 PeiCpuMpData
->BspNumber
= (UINT32
) Index1
;
107 Enable x2APIC mode on APs.
109 @param Buffer Pointer to private data buffer.
117 SetApicMode (LOCAL_APIC_MODE_X2APIC
);
121 Get CPU MP Data pointer from the Guided HOB.
123 @return Pointer to Pointer to PEI CPU MP Data
130 EFI_HOB_GUID_TYPE
*GuidHob
;
132 PEI_CPU_MP_DATA
*CpuMpData
;
135 GuidHob
= GetFirstGuidHob (&gEfiCallerIdGuid
);
136 if (GuidHob
!= NULL
) {
137 DataInHob
= GET_GUID_HOB_DATA (GuidHob
);
138 CpuMpData
= (PEI_CPU_MP_DATA
*)(*(UINTN
*)DataInHob
);
140 ASSERT (CpuMpData
!= NULL
);
145 This function will be called from AP reset code if BSP uses WakeUpAP.
147 @param ExchangeInfo Pointer to the MP exchange info buffer
148 @param NumApsExecuting Number of curret executing AP
153 IN MP_CPU_EXCHANGE_INFO
*ExchangeInfo
,
154 IN UINTN NumApsExecuting
157 PEI_CPU_MP_DATA
*PeiCpuMpData
;
158 UINTN ProcessorNumber
;
159 EFI_AP_PROCEDURE Procedure
;
162 PeiCpuMpData
= ExchangeInfo
->PeiCpuMpData
;
163 if (PeiCpuMpData
->InitFlag
) {
165 // This is first time AP wakeup, get BIST information from AP stack
167 BistData
= *(UINTN
*) (PeiCpuMpData
->Buffer
+ NumApsExecuting
* PeiCpuMpData
->CpuApStackSize
- sizeof (UINTN
));
168 PeiCpuMpData
->CpuData
[NumApsExecuting
].Health
.Uint32
= (UINT32
) BistData
;
169 PeiCpuMpData
->CpuData
[NumApsExecuting
].ApicId
= GetInitialApicId ();
170 if (PeiCpuMpData
->CpuData
[NumApsExecuting
].ApicId
>= 0xFF) {
172 // Set x2APIC mode if there are any logical processor reporting
173 // an APIC ID of 255 or greater.
175 AcquireSpinLock(&PeiCpuMpData
->MpLock
);
176 PeiCpuMpData
->X2ApicEnable
= TRUE
;
177 ReleaseSpinLock(&PeiCpuMpData
->MpLock
);
180 // Sync BSP's Mtrr table to all wakeup APs and load microcode on APs.
182 MtrrSetAllMtrrs (&PeiCpuMpData
->MtrrTable
);
186 // Execute AP function if AP is not disabled
188 GetProcessorNumber (PeiCpuMpData
, &ProcessorNumber
);
189 if ((PeiCpuMpData
->CpuData
[ProcessorNumber
].State
!= CpuStateDisabled
) &&
190 (PeiCpuMpData
->ApFunction
!= 0)) {
191 PeiCpuMpData
->CpuData
[ProcessorNumber
].State
= CpuStateBusy
;
192 Procedure
= (EFI_AP_PROCEDURE
)(UINTN
)PeiCpuMpData
->ApFunction
;
193 Procedure ((VOID
*)(UINTN
)PeiCpuMpData
->ApFunctionArgument
);
194 PeiCpuMpData
->CpuData
[ProcessorNumber
].State
= CpuStateIdle
;
199 // AP finished executing C code
201 InterlockedIncrement ((UINT32
*)&PeiCpuMpData
->FinishedCount
);
207 This function will be called by BSP to wakeup AP.
209 @param PeiCpuMpData Pointer to PEI CPU MP Data
210 @param Broadcast TRUE: Send broadcast IPI to all APs
211 FALSE: Send IPI to AP by ApicId
212 @param ApicId Apic ID for the processor to be waked
213 @param Procedure The function to be invoked by AP
214 @param ProcedureArgument The argument to be passed into AP function
218 IN PEI_CPU_MP_DATA
*PeiCpuMpData
,
219 IN BOOLEAN Broadcast
,
221 IN EFI_AP_PROCEDURE Procedure
, OPTIONAL
222 IN VOID
*ProcedureArgument OPTIONAL
225 volatile MP_CPU_EXCHANGE_INFO
*ExchangeInfo
;
227 PeiCpuMpData
->ApFunction
= (UINTN
) Procedure
;
228 PeiCpuMpData
->ApFunctionArgument
= (UINTN
) ProcedureArgument
;
229 PeiCpuMpData
->FinishedCount
= 0;
231 ExchangeInfo
= PeiCpuMpData
->MpCpuExchangeInfo
;
232 ExchangeInfo
->Lock
= 0;
233 ExchangeInfo
->StackStart
= PeiCpuMpData
->Buffer
;
234 ExchangeInfo
->StackSize
= PeiCpuMpData
->CpuApStackSize
;
235 ExchangeInfo
->BufferStart
= PeiCpuMpData
->WakeupBuffer
;
236 ExchangeInfo
->PmodeOffset
= PeiCpuMpData
->AddressMap
.PModeEntryOffset
;
237 ExchangeInfo
->LmodeOffset
= PeiCpuMpData
->AddressMap
.LModeEntryOffset
;
238 ExchangeInfo
->Cr3
= AsmReadCr3 ();
239 ExchangeInfo
->CFunction
= (UINTN
) ApCFunction
;
240 ExchangeInfo
->NumApsExecuting
= 0;
241 ExchangeInfo
->PeiCpuMpData
= PeiCpuMpData
;
244 // Get the BSP's data of GDT and IDT
246 CopyMem ((VOID
*)&ExchangeInfo
->GdtrProfile
, &mGdt
, sizeof(mGdt
));
247 AsmReadIdtr ((IA32_DESCRIPTOR
*) &ExchangeInfo
->IdtrProfile
);
250 SendInitSipiSipiAllExcludingSelf ((UINT32
) ExchangeInfo
->BufferStart
);
252 SendInitSipiSipi (ApicId
, (UINT32
) ExchangeInfo
->BufferStart
);
259 Get available system memory below 1MB by specified size.
261 @param WakeupBufferSize Wakeup buffer size required
263 @retval other Return wakeup buffer address below 1MB.
264 @retval -1 Cannot find free memory below 1MB.
268 IN UINTN WakeupBufferSize
271 EFI_PEI_HOB_POINTERS Hob
;
272 UINTN WakeupBufferStart
;
273 UINTN WakeupBufferEnd
;
276 // Get the HOB list for processing
278 Hob
.Raw
= GetHobList ();
281 // Collect memory ranges
283 while (!END_OF_HOB_LIST (Hob
)) {
284 if (Hob
.Header
->HobType
== EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
) {
285 if ((Hob
.ResourceDescriptor
->PhysicalStart
< BASE_1MB
) &&
286 (Hob
.ResourceDescriptor
->ResourceType
== EFI_RESOURCE_SYSTEM_MEMORY
) &&
287 ((Hob
.ResourceDescriptor
->ResourceAttribute
&
288 (EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED
|
289 EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED
|
290 EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED
294 // Need memory under 1MB to be collected here
296 WakeupBufferEnd
= (UINTN
) (Hob
.ResourceDescriptor
->PhysicalStart
+ Hob
.ResourceDescriptor
->ResourceLength
);
297 if (WakeupBufferEnd
> BASE_1MB
) {
299 // Wakeup buffer should be under 1MB
301 WakeupBufferEnd
= BASE_1MB
;
304 // Wakeup buffer should be aligned on 4KB
306 WakeupBufferStart
= (WakeupBufferEnd
- WakeupBufferSize
) & ~(SIZE_4KB
- 1);
307 if (WakeupBufferStart
< Hob
.ResourceDescriptor
->PhysicalStart
) {
311 // Create a memory allocation HOB.
313 BuildMemoryAllocationHob (
318 return WakeupBufferStart
;
324 Hob
.Raw
= GET_NEXT_HOB (Hob
);
331 Get available system memory below 1MB by specified size.
333 @param PeiCpuMpData Pointer to PEI CPU MP Data
336 BackupAndPrepareWakeupBuffer(
337 IN PEI_CPU_MP_DATA
*PeiCpuMpData
341 (VOID
*) PeiCpuMpData
->BackupBuffer
,
342 (VOID
*) PeiCpuMpData
->WakeupBuffer
,
343 PeiCpuMpData
->BackupBufferSize
346 (VOID
*) PeiCpuMpData
->WakeupBuffer
,
347 (VOID
*) PeiCpuMpData
->AddressMap
.RendezvousFunnelAddress
,
348 PeiCpuMpData
->AddressMap
.RendezvousFunnelSize
353 Restore wakeup buffer data.
355 @param PeiCpuMpData Pointer to PEI CPU MP Data
359 IN PEI_CPU_MP_DATA
*PeiCpuMpData
362 CopyMem ((VOID
*) PeiCpuMpData
->WakeupBuffer
, (VOID
*) PeiCpuMpData
->BackupBuffer
, PeiCpuMpData
->BackupBufferSize
);
366 This function will get CPU count in the system.
368 @param PeiCpuMpData Pointer to PEI CPU MP Data
370 @return AP processor count
373 CountProcessorNumber (
374 IN PEI_CPU_MP_DATA
*PeiCpuMpData
378 // Load Microcode on BSP
382 // Store BSP's MTRR setting
384 MtrrGetAllMtrrs (&PeiCpuMpData
->MtrrTable
);
387 // Only perform AP detection if PcdCpuMaxLogicalProcessorNumber is greater than 1
389 if (PcdGet32 (PcdCpuMaxLogicalProcessorNumber
) > 1) {
391 // Send 1st broadcast IPI to APs to wakeup APs
393 PeiCpuMpData
->InitFlag
= TRUE
;
394 PeiCpuMpData
->X2ApicEnable
= FALSE
;
395 WakeUpAP (PeiCpuMpData
, TRUE
, 0, NULL
, NULL
);
397 // Wait for AP task to complete and then exit.
399 MicroSecondDelay (PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds
));
400 PeiCpuMpData
->InitFlag
= FALSE
;
401 PeiCpuMpData
->CpuCount
+= (UINT32
)PeiCpuMpData
->MpCpuExchangeInfo
->NumApsExecuting
;
402 ASSERT (PeiCpuMpData
->CpuCount
<= PcdGet32 (PcdCpuMaxLogicalProcessorNumber
));
404 // Wait for all APs finished the initialization
406 while (PeiCpuMpData
->FinishedCount
< (PeiCpuMpData
->CpuCount
- 1)) {
410 if (PeiCpuMpData
->X2ApicEnable
) {
411 DEBUG ((EFI_D_INFO
, "Force x2APIC mode!\n"));
413 // Send 2nd broadcast IPI to all APs to enable x2APIC mode
415 WakeUpAP (PeiCpuMpData
, TRUE
, 0, ApFuncEnableX2Apic
, NULL
);
417 // Wait for all known APs finished
419 while (PeiCpuMpData
->FinishedCount
< (PeiCpuMpData
->CpuCount
- 1)) {
423 // Enable x2APIC on BSP
425 SetApicMode (LOCAL_APIC_MODE_X2APIC
);
427 DEBUG ((EFI_D_INFO
, "APIC MODE is %d\n", GetApicMode ()));
429 // Sort BSP/Aps by CPU APIC ID in ascending order
431 SortApicId (PeiCpuMpData
);
434 DEBUG ((EFI_D_INFO
, "CpuMpPei: Find %d processors in system.\n", PeiCpuMpData
->CpuCount
));
435 return PeiCpuMpData
->CpuCount
;
439 Prepare for AP wakeup buffer and copy AP reset code into it.
441 Get wakeup buffer below 1MB. Allocate memory for CPU MP Data and APs Stack.
443 @return Pointer to PEI CPU MP Data
446 PrepareAPStartupVector (
452 PEI_CPU_MP_DATA
*PeiCpuMpData
;
453 EFI_PHYSICAL_ADDRESS Buffer
;
456 UINTN WakeupBufferSize
;
457 MP_ASSEMBLY_ADDRESS_MAP AddressMap
;
459 AsmGetAddressMap (&AddressMap
);
460 WakeupBufferSize
= AddressMap
.RendezvousFunnelSize
+ sizeof (MP_CPU_EXCHANGE_INFO
);
461 WakeupBuffer
= GetWakeupBuffer ((WakeupBufferSize
+ SIZE_4KB
- 1) & ~(SIZE_4KB
- 1));
462 ASSERT (WakeupBuffer
!= (UINTN
) -1);
463 DEBUG ((EFI_D_INFO
, "CpuMpPei: WakeupBuffer = 0x%x\n", WakeupBuffer
));
466 // Allocate Pages for APs stack, CPU MP Data and backup buffer for wakeup buffer
468 MaxCpuCount
= PcdGet32(PcdCpuMaxLogicalProcessorNumber
);
469 BufferSize
= PcdGet32 (PcdCpuApStackSize
) * MaxCpuCount
+ sizeof (PEI_CPU_MP_DATA
)
470 + WakeupBufferSize
+ sizeof (PEI_CPU_DATA
) * MaxCpuCount
;
471 Status
= PeiServicesAllocatePages (
473 EFI_SIZE_TO_PAGES (BufferSize
),
476 ASSERT_EFI_ERROR (Status
);
478 PeiCpuMpData
= (PEI_CPU_MP_DATA
*) (UINTN
) (Buffer
+ PcdGet32 (PcdCpuApStackSize
) * MaxCpuCount
);
479 PeiCpuMpData
->Buffer
= (UINTN
) Buffer
;
480 PeiCpuMpData
->CpuApStackSize
= PcdGet32 (PcdCpuApStackSize
);
481 PeiCpuMpData
->WakeupBuffer
= WakeupBuffer
;
482 PeiCpuMpData
->BackupBuffer
= (UINTN
)PeiCpuMpData
+ sizeof (PEI_CPU_MP_DATA
);
483 PeiCpuMpData
->BackupBufferSize
= WakeupBufferSize
;
484 PeiCpuMpData
->MpCpuExchangeInfo
= (MP_CPU_EXCHANGE_INFO
*) (UINTN
) (WakeupBuffer
+ AddressMap
.RendezvousFunnelSize
);
486 PeiCpuMpData
->CpuCount
= 1;
487 PeiCpuMpData
->BspNumber
= 0;
488 PeiCpuMpData
->CpuData
= (PEI_CPU_DATA
*) (PeiCpuMpData
->BackupBuffer
+
489 PeiCpuMpData
->BackupBufferSize
);
490 PeiCpuMpData
->CpuData
[0].ApicId
= GetInitialApicId ();
491 PeiCpuMpData
->CpuData
[0].Health
.Uint32
= 0;
492 PeiCpuMpData
->EndOfPeiFlag
= FALSE
;
493 InitializeSpinLock(&PeiCpuMpData
->MpLock
);
494 CopyMem (&PeiCpuMpData
->AddressMap
, &AddressMap
, sizeof (MP_ASSEMBLY_ADDRESS_MAP
));
497 // Backup original data and copy AP reset code in it
499 BackupAndPrepareWakeupBuffer(PeiCpuMpData
);
505 Notify function on End Of Pei PPI.
507 On S3 boot, this function will restore wakeup buffer data.
508 On normal boot, this function will flag wakeup buffer to be un-used type.
510 @param PeiServices The pointer to the PEI Services Table.
511 @param NotifyDescriptor Address of the notification descriptor data structure.
512 @param Ppi Address of the PPI that was installed.
514 @retval EFI_SUCCESS When everything is OK.
519 CpuMpEndOfPeiCallback (
520 IN EFI_PEI_SERVICES
**PeiServices
,
521 IN EFI_PEI_NOTIFY_DESCRIPTOR
*NotifyDescriptor
,
526 EFI_BOOT_MODE BootMode
;
527 PEI_CPU_MP_DATA
*PeiCpuMpData
;
528 EFI_PEI_HOB_POINTERS Hob
;
529 EFI_HOB_MEMORY_ALLOCATION
*MemoryHob
;
531 DEBUG ((EFI_D_INFO
, "CpuMpPei: CpuMpEndOfPeiCallback () invokded\n"));
533 Status
= PeiServicesGetBootMode (&BootMode
);
534 ASSERT_EFI_ERROR (Status
);
536 PeiCpuMpData
= GetMpHobData ();
537 ASSERT (PeiCpuMpData
!= NULL
);
539 if (BootMode
!= BOOT_ON_S3_RESUME
) {
541 // Get the HOB list for processing
543 Hob
.Raw
= GetHobList ();
545 // Collect memory ranges
547 while (!END_OF_HOB_LIST (Hob
)) {
548 if (Hob
.Header
->HobType
== EFI_HOB_TYPE_MEMORY_ALLOCATION
) {
549 MemoryHob
= Hob
.MemoryAllocation
;
550 if(MemoryHob
->AllocDescriptor
.MemoryBaseAddress
== PeiCpuMpData
->WakeupBuffer
) {
552 // Flag this HOB type to un-used
554 GET_HOB_TYPE (Hob
) = EFI_HOB_TYPE_UNUSED
;
558 Hob
.Raw
= GET_NEXT_HOB (Hob
);
561 RestoreWakeupBuffer (PeiCpuMpData
);
562 PeiCpuMpData
->EndOfPeiFlag
= TRUE
;
568 The Entry point of the MP CPU PEIM.
570 This function will wakeup APs and collect CPU AP count and install the
573 @param FileHandle Handle of the file being invoked.
574 @param PeiServices Describes the list of possible PEI Services.
576 @retval EFI_SUCCESS MpServicePpi is installed successfully.
582 IN EFI_PEI_FILE_HANDLE FileHandle
,
583 IN CONST EFI_PEI_SERVICES
**PeiServices
587 PEI_CPU_MP_DATA
*PeiCpuMpData
;
588 UINT32 ProcessorCount
;
591 // Load new GDT table on BSP
593 AsmInitializeGdt (&mGdt
);
595 // Get wakeup buffer and copy AP reset code in it
597 PeiCpuMpData
= PrepareAPStartupVector ();
599 // Count processor number and collect processor information
601 ProcessorCount
= CountProcessorNumber (PeiCpuMpData
);
603 // Build location of PEI CPU MP DATA buffer in HOB
607 (VOID
*)&PeiCpuMpData
,
611 // Update and publish CPU BIST information
613 CollectBistDataFromPpi (PeiServices
, PeiCpuMpData
);
615 // register an event for EndOfPei
617 Status
= PeiServicesNotifyPpi (&mNotifyList
);
618 ASSERT_EFI_ERROR (Status
);
620 // Install CPU MP PPI
622 Status
= PeiServicesInstallPpi(&mPeiCpuMpPpiDesc
);
623 ASSERT_EFI_ERROR (Status
);