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