]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/CpuMpPei/CpuMpPei.c
UefiCpuPkg/CpuMpPei: Place APs in proper loop mode after AP execution
[mirror_edk2.git] / UefiCpuPkg / CpuMpPei / CpuMpPei.c
1 /** @file
2 CPU PEI Module installs CPU Multiple Processor PPI.
3
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
9
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.
12
13 **/
14
15 #include "CpuMpPei.h"
16
17 //
18 // Global Descriptor Table (GDT)
19 //
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
31 };
32
33 //
34 // IA32 Gdt register
35 //
36 GLOBAL_REMOVE_IF_UNREFERENCED IA32_DESCRIPTOR mGdt = {
37 sizeof (mGdtEntries) - 1,
38 (UINTN) mGdtEntries
39 };
40
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,
44 CpuMpEndOfPeiCallback
45 };
46
47 /**
48 Sort the APIC ID of all processors.
49
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.
52
53 @param PeiCpuMpData Pointer to PEI CPU MP Data
54 **/
55 VOID
56 SortApicId (
57 IN PEI_CPU_MP_DATA *PeiCpuMpData
58 )
59 {
60 UINTN Index1;
61 UINTN Index2;
62 UINTN Index3;
63 UINT32 ApicId;
64 PEI_CPU_DATA CpuData;
65 UINT32 ApCount;
66
67 ApCount = PeiCpuMpData->CpuCount - 1;
68
69 if (ApCount != 0) {
70 for (Index1 = 0; Index1 < ApCount; Index1++) {
71 Index3 = Index1;
72 //
73 // Sort key is the hardware default APIC ID
74 //
75 ApicId = PeiCpuMpData->CpuData[Index1].ApicId;
76 for (Index2 = Index1 + 1; Index2 <= ApCount; Index2++) {
77 if (ApicId > PeiCpuMpData->CpuData[Index2].ApicId) {
78 Index3 = Index2;
79 ApicId = PeiCpuMpData->CpuData[Index2].ApicId;
80 }
81 }
82 if (Index3 != Index1) {
83 CopyMem (&CpuData, &PeiCpuMpData->CpuData[Index3], sizeof (PEI_CPU_DATA));
84 CopyMem (
85 &PeiCpuMpData->CpuData[Index3],
86 &PeiCpuMpData->CpuData[Index1],
87 sizeof (PEI_CPU_DATA)
88 );
89 CopyMem (&PeiCpuMpData->CpuData[Index1], &CpuData, sizeof (PEI_CPU_DATA));
90 }
91 }
92
93 //
94 // Get the processor number for the BSP
95 //
96 ApicId = GetInitialApicId ();
97 for (Index1 = 0; Index1 < PeiCpuMpData->CpuCount; Index1++) {
98 if (PeiCpuMpData->CpuData[Index1].ApicId == ApicId) {
99 PeiCpuMpData->BspNumber = (UINT32) Index1;
100 break;
101 }
102 }
103 }
104 }
105
106 /**
107 Enable x2APIC mode on APs.
108
109 @param Buffer Pointer to private data buffer.
110 **/
111 VOID
112 EFIAPI
113 ApFuncEnableX2Apic (
114 IN OUT VOID *Buffer
115 )
116 {
117 SetApicMode (LOCAL_APIC_MODE_X2APIC);
118 }
119
120 /**
121 Get AP loop mode.
122
123 @param MonitorFilterSize Returns the largest monitor-line size in bytes.
124
125 @return The AP loop mode.
126 **/
127 UINT8
128 GetApLoopMode (
129 OUT UINT16 *MonitorFilterSize
130 )
131 {
132 UINT8 ApLoopMode;
133 UINT32 RegEbx;
134 UINT32 RegEcx;
135 UINT32 RegEdx;
136
137 ASSERT (MonitorFilterSize != NULL);
138
139 ApLoopMode = PcdGet8 (PcdCpuApLoopMode);
140 ASSERT (ApLoopMode >= ApInHltLoop && ApLoopMode <= ApInRunLoop);
141 if (ApLoopMode == ApInMwaitLoop) {
142 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, &RegEcx, NULL);
143 if ((RegEcx & BIT3) == 0) {
144 //
145 // If processor does not support MONITOR/MWAIT feature
146 // by CPUID.[EAX=01H]:ECX.BIT3, force AP in Hlt-loop mode
147 //
148 ApLoopMode = ApInHltLoop;
149 }
150 }
151
152 if (ApLoopMode == ApInHltLoop) {
153 *MonitorFilterSize = 0;
154 } else if (ApLoopMode == ApInRunLoop) {
155 *MonitorFilterSize = sizeof (UINT32);
156 } else if (ApLoopMode == ApInMwaitLoop) {
157 //
158 // CPUID.[EAX=05H]:EBX.BIT0-15: Largest monitor-line size in bytes
159 // CPUID.[EAX=05H].EDX: C-states supported using MWAIT
160 //
161 AsmCpuid (CPUID_MONITOR_MWAIT, NULL, &RegEbx, NULL, &RegEdx);
162 *MonitorFilterSize = RegEbx & 0xFFFF;
163 }
164
165 return ApLoopMode;
166 }
167
168 /**
169 Get CPU MP Data pointer from the Guided HOB.
170
171 @return Pointer to Pointer to PEI CPU MP Data
172 **/
173 PEI_CPU_MP_DATA *
174 GetMpHobData (
175 VOID
176 )
177 {
178 EFI_HOB_GUID_TYPE *GuidHob;
179 VOID *DataInHob;
180 PEI_CPU_MP_DATA *CpuMpData;
181
182 CpuMpData = NULL;
183 GuidHob = GetFirstGuidHob (&gEfiCallerIdGuid);
184 if (GuidHob != NULL) {
185 DataInHob = GET_GUID_HOB_DATA (GuidHob);
186 CpuMpData = (PEI_CPU_MP_DATA *)(*(UINTN *)DataInHob);
187 }
188 ASSERT (CpuMpData != NULL);
189 return CpuMpData;
190 }
191
192 /**
193 Save the volatile registers required to be restored following INIT IPI.
194
195 @param VolatileRegisters Returns buffer saved the volatile resisters
196 **/
197 VOID
198 SaveVolatileRegisters (
199 OUT CPU_VOLATILE_REGISTERS *VolatileRegisters
200 )
201 {
202 UINT32 RegEdx;
203
204 VolatileRegisters->Cr0 = AsmReadCr0 ();
205 VolatileRegisters->Cr3 = AsmReadCr3 ();
206 VolatileRegisters->Cr4 = AsmReadCr4 ();
207
208 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx);
209 if ((RegEdx & BIT2) != 0) {
210 //
211 // If processor supports Debugging Extensions feature
212 // by CPUID.[EAX=01H]:EDX.BIT2
213 //
214 VolatileRegisters->Dr0 = AsmReadDr0 ();
215 VolatileRegisters->Dr1 = AsmReadDr1 ();
216 VolatileRegisters->Dr2 = AsmReadDr2 ();
217 VolatileRegisters->Dr3 = AsmReadDr3 ();
218 VolatileRegisters->Dr6 = AsmReadDr6 ();
219 VolatileRegisters->Dr7 = AsmReadDr7 ();
220 }
221 }
222
223 /**
224 Restore the volatile registers following INIT IPI.
225
226 @param VolatileRegisters Pointer to volatile resisters
227 @param IsRestoreDr TRUE: Restore DRx if supported
228 FALSE: Do not restore DRx
229 **/
230 VOID
231 RestoreVolatileRegisters (
232 IN CPU_VOLATILE_REGISTERS *VolatileRegisters,
233 IN BOOLEAN IsRestoreDr
234 )
235 {
236 UINT32 RegEdx;
237
238 AsmWriteCr0 (VolatileRegisters->Cr0);
239 AsmWriteCr3 (VolatileRegisters->Cr3);
240 AsmWriteCr4 (VolatileRegisters->Cr4);
241
242 if (IsRestoreDr) {
243 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx);
244 if ((RegEdx & BIT2) != 0) {
245 //
246 // If processor supports Debugging Extensions feature
247 // by CPUID.[EAX=01H]:EDX.BIT2
248 //
249 AsmWriteDr0 (VolatileRegisters->Dr0);
250 AsmWriteDr1 (VolatileRegisters->Dr1);
251 AsmWriteDr2 (VolatileRegisters->Dr2);
252 AsmWriteDr3 (VolatileRegisters->Dr3);
253 AsmWriteDr6 (VolatileRegisters->Dr6);
254 AsmWriteDr7 (VolatileRegisters->Dr7);
255 }
256 }
257 }
258
259 /**
260 This function will be called from AP reset code if BSP uses WakeUpAP.
261
262 @param ExchangeInfo Pointer to the MP exchange info buffer
263 @param NumApsExecuting Number of current executing AP
264 **/
265 VOID
266 EFIAPI
267 ApCFunction (
268 IN MP_CPU_EXCHANGE_INFO *ExchangeInfo,
269 IN UINTN NumApsExecuting
270 )
271 {
272 PEI_CPU_MP_DATA *PeiCpuMpData;
273 UINTN ProcessorNumber;
274 EFI_AP_PROCEDURE Procedure;
275 UINTN BistData;
276 volatile UINT32 *ApStartupSignalBuffer;
277
278 PeiCpuMpData = ExchangeInfo->PeiCpuMpData;
279 while (TRUE) {
280 if (PeiCpuMpData->InitFlag) {
281 ProcessorNumber = NumApsExecuting;
282 //
283 // Sync BSP's Control registers to APs
284 //
285 RestoreVolatileRegisters (&PeiCpuMpData->CpuData[0].VolatileRegisters, FALSE);
286 //
287 // This is first time AP wakeup, get BIST information from AP stack
288 //
289 BistData = *(UINTN *) (PeiCpuMpData->Buffer + ProcessorNumber * PeiCpuMpData->CpuApStackSize - sizeof (UINTN));
290 PeiCpuMpData->CpuData[ProcessorNumber].Health.Uint32 = (UINT32) BistData;
291 PeiCpuMpData->CpuData[ProcessorNumber].ApicId = GetInitialApicId ();
292 if (PeiCpuMpData->CpuData[ProcessorNumber].ApicId >= 0xFF) {
293 //
294 // Set x2APIC mode if there are any logical processor reporting
295 // an APIC ID of 255 or greater.
296 //
297 AcquireSpinLock(&PeiCpuMpData->MpLock);
298 PeiCpuMpData->X2ApicEnable = TRUE;
299 ReleaseSpinLock(&PeiCpuMpData->MpLock);
300 }
301 //
302 // Sync BSP's Mtrr table to all wakeup APs and load microcode on APs.
303 //
304 MtrrSetAllMtrrs (&PeiCpuMpData->MtrrTable);
305 MicrocodeDetect ();
306 PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle;
307 } else {
308 //
309 // Execute AP function if AP is not disabled
310 //
311 GetProcessorNumber (PeiCpuMpData, &ProcessorNumber);
312 if (PeiCpuMpData->ApLoopMode == ApInHltLoop) {
313 //
314 // Restore AP's volatile registers saved
315 //
316 RestoreVolatileRegisters (&PeiCpuMpData->CpuData[ProcessorNumber].VolatileRegisters, TRUE);
317 }
318
319 if ((PeiCpuMpData->CpuData[ProcessorNumber].State != CpuStateDisabled) &&
320 (PeiCpuMpData->ApFunction != 0)) {
321 PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateBusy;
322 Procedure = (EFI_AP_PROCEDURE)(UINTN)PeiCpuMpData->ApFunction;
323 //
324 // Invoke AP function here
325 //
326 Procedure ((VOID *)(UINTN)PeiCpuMpData->ApFunctionArgument);
327 //
328 // Re-get the processor number due to BSP/AP maybe exchange in AP function
329 //
330 GetProcessorNumber (PeiCpuMpData, &ProcessorNumber);
331 PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle;
332 }
333 }
334
335 //
336 // AP finished executing C code
337 //
338 InterlockedIncrement ((UINT32 *)&PeiCpuMpData->FinishedCount);
339
340 //
341 // Place AP is specified loop mode
342 //
343 if (PeiCpuMpData->ApLoopMode == ApInHltLoop) {
344 //
345 // Save AP volatile registers
346 //
347 SaveVolatileRegisters (&PeiCpuMpData->CpuData[ProcessorNumber].VolatileRegisters);
348 //
349 // Place AP in Hlt-loop
350 //
351 while (TRUE) {
352 DisableInterrupts ();
353 CpuSleep ();
354 CpuPause ();
355 }
356 }
357 ApStartupSignalBuffer = PeiCpuMpData->CpuData[ProcessorNumber].StartupApSignal;
358 //
359 // Clear AP start-up signal
360 //
361 *ApStartupSignalBuffer = 0;
362 while (TRUE) {
363 DisableInterrupts ();
364 if (PeiCpuMpData->ApLoopMode == ApInMwaitLoop) {
365 //
366 // Place AP in Mwait-loop
367 //
368 AsmMonitor ((UINTN)ApStartupSignalBuffer, 0, 0);
369 if (*ApStartupSignalBuffer != WAKEUP_AP_SIGNAL) {
370 //
371 // If AP start-up signal is not set, place AP into
372 // the maximum C-state
373 //
374 AsmMwait (PeiCpuMpData->ApTargetCState << 4, 0);
375 }
376 } else if (PeiCpuMpData->ApLoopMode == ApInRunLoop) {
377 //
378 // Place AP in Run-loop
379 //
380 CpuPause ();
381 } else {
382 ASSERT (FALSE);
383 }
384
385 //
386 // If AP start-up signal is written, AP is waken up
387 // otherwise place AP in loop again
388 //
389 if (*ApStartupSignalBuffer == WAKEUP_AP_SIGNAL) {
390 break;
391 }
392 }
393 }
394 }
395
396 /**
397 This function will be called by BSP to wakeup AP.
398
399 @param PeiCpuMpData Pointer to PEI CPU MP Data
400 @param Broadcast TRUE: Send broadcast IPI to all APs
401 FALSE: Send IPI to AP by ApicId
402 @param ApicId Apic ID for the processor to be waked
403 @param Procedure The function to be invoked by AP
404 @param ProcedureArgument The argument to be passed into AP function
405 **/
406 VOID
407 WakeUpAP (
408 IN PEI_CPU_MP_DATA *PeiCpuMpData,
409 IN BOOLEAN Broadcast,
410 IN UINT32 ApicId,
411 IN EFI_AP_PROCEDURE Procedure, OPTIONAL
412 IN VOID *ProcedureArgument OPTIONAL
413 )
414 {
415 volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;
416
417 PeiCpuMpData->ApFunction = (UINTN) Procedure;
418 PeiCpuMpData->ApFunctionArgument = (UINTN) ProcedureArgument;
419 PeiCpuMpData->FinishedCount = 0;
420
421 ExchangeInfo = PeiCpuMpData->MpCpuExchangeInfo;
422 ExchangeInfo->Lock = 0;
423 ExchangeInfo->StackStart = PeiCpuMpData->Buffer;
424 ExchangeInfo->StackSize = PeiCpuMpData->CpuApStackSize;
425 ExchangeInfo->BufferStart = PeiCpuMpData->WakeupBuffer;
426 ExchangeInfo->PmodeOffset = PeiCpuMpData->AddressMap.PModeEntryOffset;
427 ExchangeInfo->LmodeOffset = PeiCpuMpData->AddressMap.LModeEntryOffset;
428 ExchangeInfo->Cr3 = AsmReadCr3 ();
429 ExchangeInfo->CFunction = (UINTN) ApCFunction;
430 ExchangeInfo->NumApsExecuting = 0;
431 ExchangeInfo->PeiCpuMpData = PeiCpuMpData;
432
433 //
434 // Get the BSP's data of GDT and IDT
435 //
436 CopyMem ((VOID *)&ExchangeInfo->GdtrProfile, &mGdt, sizeof(mGdt));
437 AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile);
438
439 if (Broadcast) {
440 SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart);
441 } else {
442 SendInitSipiSipi (ApicId, (UINT32) ExchangeInfo->BufferStart);
443 }
444
445 return ;
446 }
447
448 /**
449 Get available system memory below 1MB by specified size.
450
451 @param WakeupBufferSize Wakeup buffer size required
452
453 @retval other Return wakeup buffer address below 1MB.
454 @retval -1 Cannot find free memory below 1MB.
455 **/
456 UINTN
457 GetWakeupBuffer (
458 IN UINTN WakeupBufferSize
459 )
460 {
461 EFI_PEI_HOB_POINTERS Hob;
462 UINTN WakeupBufferStart;
463 UINTN WakeupBufferEnd;
464
465 //
466 // Get the HOB list for processing
467 //
468 Hob.Raw = GetHobList ();
469
470 //
471 // Collect memory ranges
472 //
473 while (!END_OF_HOB_LIST (Hob)) {
474 if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
475 if ((Hob.ResourceDescriptor->PhysicalStart < BASE_1MB) &&
476 (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) &&
477 ((Hob.ResourceDescriptor->ResourceAttribute &
478 (EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED |
479 EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED |
480 EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED
481 )) == 0)
482 ) {
483 //
484 // Need memory under 1MB to be collected here
485 //
486 WakeupBufferEnd = (UINTN) (Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength);
487 if (WakeupBufferEnd > BASE_1MB) {
488 //
489 // Wakeup buffer should be under 1MB
490 //
491 WakeupBufferEnd = BASE_1MB;
492 }
493 //
494 // Wakeup buffer should be aligned on 4KB
495 //
496 WakeupBufferStart = (WakeupBufferEnd - WakeupBufferSize) & ~(SIZE_4KB - 1);
497 if (WakeupBufferStart < Hob.ResourceDescriptor->PhysicalStart) {
498 continue;
499 }
500 //
501 // Create a memory allocation HOB.
502 //
503 BuildMemoryAllocationHob (
504 WakeupBufferStart,
505 WakeupBufferSize,
506 EfiBootServicesData
507 );
508 return WakeupBufferStart;
509 }
510 }
511 //
512 // Find the next HOB
513 //
514 Hob.Raw = GET_NEXT_HOB (Hob);
515 }
516
517 return (UINTN) -1;
518 }
519
520 /**
521 Get available system memory below 1MB by specified size.
522
523 @param PeiCpuMpData Pointer to PEI CPU MP Data
524 **/
525 VOID
526 BackupAndPrepareWakeupBuffer(
527 IN PEI_CPU_MP_DATA *PeiCpuMpData
528 )
529 {
530 CopyMem (
531 (VOID *) PeiCpuMpData->BackupBuffer,
532 (VOID *) PeiCpuMpData->WakeupBuffer,
533 PeiCpuMpData->BackupBufferSize
534 );
535 CopyMem (
536 (VOID *) PeiCpuMpData->WakeupBuffer,
537 (VOID *) PeiCpuMpData->AddressMap.RendezvousFunnelAddress,
538 PeiCpuMpData->AddressMap.RendezvousFunnelSize
539 );
540 }
541
542 /**
543 Restore wakeup buffer data.
544
545 @param PeiCpuMpData Pointer to PEI CPU MP Data
546 **/
547 VOID
548 RestoreWakeupBuffer(
549 IN PEI_CPU_MP_DATA *PeiCpuMpData
550 )
551 {
552 CopyMem ((VOID *) PeiCpuMpData->WakeupBuffer, (VOID *) PeiCpuMpData->BackupBuffer, PeiCpuMpData->BackupBufferSize);
553 }
554
555 /**
556 This function will get CPU count in the system.
557
558 @param PeiCpuMpData Pointer to PEI CPU MP Data
559
560 @return AP processor count
561 **/
562 UINT32
563 CountProcessorNumber (
564 IN PEI_CPU_MP_DATA *PeiCpuMpData
565 )
566 {
567 //
568 // Load Microcode on BSP
569 //
570 MicrocodeDetect ();
571 //
572 // Store BSP's MTRR setting
573 //
574 MtrrGetAllMtrrs (&PeiCpuMpData->MtrrTable);
575
576 //
577 // Only perform AP detection if PcdCpuMaxLogicalProcessorNumber is greater than 1
578 //
579 if (PcdGet32 (PcdCpuMaxLogicalProcessorNumber) > 1) {
580 //
581 // Send 1st broadcast IPI to APs to wakeup APs
582 //
583 PeiCpuMpData->InitFlag = TRUE;
584 PeiCpuMpData->X2ApicEnable = FALSE;
585 WakeUpAP (PeiCpuMpData, TRUE, 0, NULL, NULL);
586 //
587 // Wait for AP task to complete and then exit.
588 //
589 MicroSecondDelay (PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds));
590 PeiCpuMpData->InitFlag = FALSE;
591 PeiCpuMpData->CpuCount += (UINT32)PeiCpuMpData->MpCpuExchangeInfo->NumApsExecuting;
592 ASSERT (PeiCpuMpData->CpuCount <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
593 //
594 // Wait for all APs finished the initialization
595 //
596 while (PeiCpuMpData->FinishedCount < (PeiCpuMpData->CpuCount - 1)) {
597 CpuPause ();
598 }
599
600 if (PeiCpuMpData->X2ApicEnable) {
601 DEBUG ((EFI_D_INFO, "Force x2APIC mode!\n"));
602 //
603 // Send 2nd broadcast IPI to all APs to enable x2APIC mode
604 //
605 WakeUpAP (PeiCpuMpData, TRUE, 0, ApFuncEnableX2Apic, NULL);
606 //
607 // Wait for all known APs finished
608 //
609 while (PeiCpuMpData->FinishedCount < (PeiCpuMpData->CpuCount - 1)) {
610 CpuPause ();
611 }
612 //
613 // Enable x2APIC on BSP
614 //
615 SetApicMode (LOCAL_APIC_MODE_X2APIC);
616 }
617 DEBUG ((EFI_D_INFO, "APIC MODE is %d\n", GetApicMode ()));
618 //
619 // Sort BSP/Aps by CPU APIC ID in ascending order
620 //
621 SortApicId (PeiCpuMpData);
622 }
623
624 DEBUG ((EFI_D_INFO, "CpuMpPei: Find %d processors in system.\n", PeiCpuMpData->CpuCount));
625 return PeiCpuMpData->CpuCount;
626 }
627
628 /**
629 Prepare for AP wakeup buffer and copy AP reset code into it.
630
631 Get wakeup buffer below 1MB. Allocate memory for CPU MP Data and APs Stack.
632
633 @return Pointer to PEI CPU MP Data
634 **/
635 PEI_CPU_MP_DATA *
636 PrepareAPStartupVector (
637 VOID
638 )
639 {
640 EFI_STATUS Status;
641 UINT32 MaxCpuCount;
642 PEI_CPU_MP_DATA *PeiCpuMpData;
643 EFI_PHYSICAL_ADDRESS Buffer;
644 UINTN BufferSize;
645 UINTN WakeupBuffer;
646 UINTN WakeupBufferSize;
647 MP_ASSEMBLY_ADDRESS_MAP AddressMap;
648 UINT8 ApLoopMode;
649 UINT16 MonitorFilterSize;
650 UINT8 *MonitorBuffer;
651 UINTN Index;
652
653 AsmGetAddressMap (&AddressMap);
654 WakeupBufferSize = AddressMap.RendezvousFunnelSize + sizeof (MP_CPU_EXCHANGE_INFO);
655 WakeupBuffer = GetWakeupBuffer ((WakeupBufferSize + SIZE_4KB - 1) & ~(SIZE_4KB - 1));
656 ASSERT (WakeupBuffer != (UINTN) -1);
657 DEBUG ((EFI_D_INFO, "CpuMpPei: WakeupBuffer = 0x%x\n", WakeupBuffer));
658
659 //
660 // Allocate Pages for APs stack, CPU MP Data, backup buffer for wakeup buffer,
661 // and monitor buffer if required.
662 //
663 MaxCpuCount = PcdGet32(PcdCpuMaxLogicalProcessorNumber);
664 BufferSize = PcdGet32 (PcdCpuApStackSize) * MaxCpuCount + sizeof (PEI_CPU_MP_DATA)
665 + WakeupBufferSize + sizeof (PEI_CPU_DATA) * MaxCpuCount;
666 ApLoopMode = GetApLoopMode (&MonitorFilterSize);
667 BufferSize += MonitorFilterSize * MaxCpuCount;
668 Status = PeiServicesAllocatePages (
669 EfiBootServicesData,
670 EFI_SIZE_TO_PAGES (BufferSize),
671 &Buffer
672 );
673 ASSERT_EFI_ERROR (Status);
674
675 PeiCpuMpData = (PEI_CPU_MP_DATA *) (UINTN) (Buffer + PcdGet32 (PcdCpuApStackSize) * MaxCpuCount);
676 PeiCpuMpData->Buffer = (UINTN) Buffer;
677 PeiCpuMpData->CpuApStackSize = PcdGet32 (PcdCpuApStackSize);
678 PeiCpuMpData->WakeupBuffer = WakeupBuffer;
679 PeiCpuMpData->BackupBuffer = (UINTN)PeiCpuMpData + sizeof (PEI_CPU_MP_DATA);
680 PeiCpuMpData->BackupBufferSize = WakeupBufferSize;
681 PeiCpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) (WakeupBuffer + AddressMap.RendezvousFunnelSize);
682
683 PeiCpuMpData->CpuCount = 1;
684 PeiCpuMpData->BspNumber = 0;
685 PeiCpuMpData->CpuData = (PEI_CPU_DATA *) (PeiCpuMpData->BackupBuffer +
686 PeiCpuMpData->BackupBufferSize);
687 PeiCpuMpData->CpuData[0].ApicId = GetInitialApicId ();
688 PeiCpuMpData->CpuData[0].Health.Uint32 = 0;
689 PeiCpuMpData->EndOfPeiFlag = FALSE;
690 InitializeSpinLock(&PeiCpuMpData->MpLock);
691 SaveVolatileRegisters (&PeiCpuMpData->CpuData[0].VolatileRegisters);
692 CopyMem (&PeiCpuMpData->AddressMap, &AddressMap, sizeof (MP_ASSEMBLY_ADDRESS_MAP));
693 //
694 // Initialize AP loop mode
695 //
696 PeiCpuMpData->ApLoopMode = ApLoopMode;
697 DEBUG ((EFI_D_INFO, "AP Loop Mode is %d\n", PeiCpuMpData->ApLoopMode));
698 MonitorBuffer = (UINT8 *)(PeiCpuMpData->CpuData + MaxCpuCount);
699 if (PeiCpuMpData->ApLoopMode != ApInHltLoop) {
700 //
701 // Set up APs wakeup signal buffer
702 //
703 for (Index = 0; Index < MaxCpuCount; Index++) {
704 PeiCpuMpData->CpuData[Index].StartupApSignal =
705 (UINT32 *)(MonitorBuffer + MonitorFilterSize * Index);
706 }
707 }
708 //
709 // Backup original data and copy AP reset code in it
710 //
711 BackupAndPrepareWakeupBuffer(PeiCpuMpData);
712
713 return PeiCpuMpData;
714 }
715
716 /**
717 Notify function on End Of Pei PPI.
718
719 On S3 boot, this function will restore wakeup buffer data.
720 On normal boot, this function will flag wakeup buffer to be un-used type.
721
722 @param PeiServices The pointer to the PEI Services Table.
723 @param NotifyDescriptor Address of the notification descriptor data structure.
724 @param Ppi Address of the PPI that was installed.
725
726 @retval EFI_SUCCESS When everything is OK.
727
728 **/
729 EFI_STATUS
730 EFIAPI
731 CpuMpEndOfPeiCallback (
732 IN EFI_PEI_SERVICES **PeiServices,
733 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
734 IN VOID *Ppi
735 )
736 {
737 EFI_STATUS Status;
738 EFI_BOOT_MODE BootMode;
739 PEI_CPU_MP_DATA *PeiCpuMpData;
740 EFI_PEI_HOB_POINTERS Hob;
741 EFI_HOB_MEMORY_ALLOCATION *MemoryHob;
742
743 DEBUG ((EFI_D_INFO, "CpuMpPei: CpuMpEndOfPeiCallback () invoked\n"));
744
745 Status = PeiServicesGetBootMode (&BootMode);
746 ASSERT_EFI_ERROR (Status);
747
748 PeiCpuMpData = GetMpHobData ();
749 ASSERT (PeiCpuMpData != NULL);
750
751 if (BootMode != BOOT_ON_S3_RESUME) {
752 //
753 // Get the HOB list for processing
754 //
755 Hob.Raw = GetHobList ();
756 //
757 // Collect memory ranges
758 //
759 while (!END_OF_HOB_LIST (Hob)) {
760 if (Hob.Header->HobType == EFI_HOB_TYPE_MEMORY_ALLOCATION) {
761 MemoryHob = Hob.MemoryAllocation;
762 if(MemoryHob->AllocDescriptor.MemoryBaseAddress == PeiCpuMpData->WakeupBuffer) {
763 //
764 // Flag this HOB type to un-used
765 //
766 GET_HOB_TYPE (Hob) = EFI_HOB_TYPE_UNUSED;
767 break;
768 }
769 }
770 Hob.Raw = GET_NEXT_HOB (Hob);
771 }
772 } else {
773 RestoreWakeupBuffer (PeiCpuMpData);
774 PeiCpuMpData->EndOfPeiFlag = TRUE;
775 }
776 return EFI_SUCCESS;
777 }
778
779 /**
780 The Entry point of the MP CPU PEIM.
781
782 This function will wakeup APs and collect CPU AP count and install the
783 Mp Service Ppi.
784
785 @param FileHandle Handle of the file being invoked.
786 @param PeiServices Describes the list of possible PEI Services.
787
788 @retval EFI_SUCCESS MpServicePpi is installed successfully.
789
790 **/
791 EFI_STATUS
792 EFIAPI
793 CpuMpPeimInit (
794 IN EFI_PEI_FILE_HANDLE FileHandle,
795 IN CONST EFI_PEI_SERVICES **PeiServices
796 )
797 {
798 EFI_STATUS Status;
799 PEI_CPU_MP_DATA *PeiCpuMpData;
800 UINT32 ProcessorCount;
801
802 //
803 // Load new GDT table on BSP
804 //
805 AsmInitializeGdt (&mGdt);
806 //
807 // Get wakeup buffer and copy AP reset code in it
808 //
809 PeiCpuMpData = PrepareAPStartupVector ();
810 //
811 // Count processor number and collect processor information
812 //
813 ProcessorCount = CountProcessorNumber (PeiCpuMpData);
814 //
815 // Build location of PEI CPU MP DATA buffer in HOB
816 //
817 BuildGuidDataHob (
818 &gEfiCallerIdGuid,
819 (VOID *)&PeiCpuMpData,
820 sizeof(UINT64)
821 );
822 //
823 // Update and publish CPU BIST information
824 //
825 CollectBistDataFromPpi (PeiServices, PeiCpuMpData);
826 //
827 // register an event for EndOfPei
828 //
829 Status = PeiServicesNotifyPpi (&mNotifyList);
830 ASSERT_EFI_ERROR (Status);
831 //
832 // Install CPU MP PPI
833 //
834 Status = PeiServicesInstallPpi(&mPeiCpuMpPpiDesc);
835 ASSERT_EFI_ERROR (Status);
836
837 return Status;
838 }