]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - UefiCpuPkg/Library/MpInitLib/MpLib.c
UefiCpuPkg: Allow AP booting under SEV-ES
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / MpLib.c
... / ...
CommitLineData
1/** @file\r
2 CPU MP Initialize Library common functions.\r
3\r
4 Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.<BR>\r
5 Copyright (c) 2020, AMD Inc. All rights reserved.<BR>\r
6\r
7 SPDX-License-Identifier: BSD-2-Clause-Patent\r
8\r
9**/\r
10\r
11#include "MpLib.h"\r
12#include <Library/VmgExitLib.h>\r
13#include <Register/Amd/Fam17Msr.h>\r
14#include <Register/Amd/Ghcb.h>\r
15\r
16EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID;\r
17\r
18\r
19/**\r
20 The function will check if BSP Execute Disable is enabled.\r
21\r
22 DxeIpl may have enabled Execute Disable for BSP, APs need to\r
23 get the status and sync up the settings.\r
24 If BSP's CR0.Paging is not set, BSP execute Disble feature is\r
25 not working actually.\r
26\r
27 @retval TRUE BSP Execute Disable is enabled.\r
28 @retval FALSE BSP Execute Disable is not enabled.\r
29**/\r
30BOOLEAN\r
31IsBspExecuteDisableEnabled (\r
32 VOID\r
33 )\r
34{\r
35 UINT32 Eax;\r
36 CPUID_EXTENDED_CPU_SIG_EDX Edx;\r
37 MSR_IA32_EFER_REGISTER EferMsr;\r
38 BOOLEAN Enabled;\r
39 IA32_CR0 Cr0;\r
40\r
41 Enabled = FALSE;\r
42 Cr0.UintN = AsmReadCr0 ();\r
43 if (Cr0.Bits.PG != 0) {\r
44 //\r
45 // If CR0 Paging bit is set\r
46 //\r
47 AsmCpuid (CPUID_EXTENDED_FUNCTION, &Eax, NULL, NULL, NULL);\r
48 if (Eax >= CPUID_EXTENDED_CPU_SIG) {\r
49 AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &Edx.Uint32);\r
50 //\r
51 // CPUID 0x80000001\r
52 // Bit 20: Execute Disable Bit available.\r
53 //\r
54 if (Edx.Bits.NX != 0) {\r
55 EferMsr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);\r
56 //\r
57 // MSR 0xC0000080\r
58 // Bit 11: Execute Disable Bit enable.\r
59 //\r
60 if (EferMsr.Bits.NXE != 0) {\r
61 Enabled = TRUE;\r
62 }\r
63 }\r
64 }\r
65 }\r
66\r
67 return Enabled;\r
68}\r
69\r
70/**\r
71 Worker function for SwitchBSP().\r
72\r
73 Worker function for SwitchBSP(), assigned to the AP which is intended\r
74 to become BSP.\r
75\r
76 @param[in] Buffer Pointer to CPU MP Data\r
77**/\r
78VOID\r
79EFIAPI\r
80FutureBSPProc (\r
81 IN VOID *Buffer\r
82 )\r
83{\r
84 CPU_MP_DATA *DataInHob;\r
85\r
86 DataInHob = (CPU_MP_DATA *) Buffer;\r
87 AsmExchangeRole (&DataInHob->APInfo, &DataInHob->BSPInfo);\r
88}\r
89\r
90/**\r
91 Get the Application Processors state.\r
92\r
93 @param[in] CpuData The pointer to CPU_AP_DATA of specified AP\r
94\r
95 @return The AP status\r
96**/\r
97CPU_STATE\r
98GetApState (\r
99 IN CPU_AP_DATA *CpuData\r
100 )\r
101{\r
102 return CpuData->State;\r
103}\r
104\r
105/**\r
106 Set the Application Processors state.\r
107\r
108 @param[in] CpuData The pointer to CPU_AP_DATA of specified AP\r
109 @param[in] State The AP status\r
110**/\r
111VOID\r
112SetApState (\r
113 IN CPU_AP_DATA *CpuData,\r
114 IN CPU_STATE State\r
115 )\r
116{\r
117 AcquireSpinLock (&CpuData->ApLock);\r
118 CpuData->State = State;\r
119 ReleaseSpinLock (&CpuData->ApLock);\r
120}\r
121\r
122/**\r
123 Save BSP's local APIC timer setting.\r
124\r
125 @param[in] CpuMpData Pointer to CPU MP Data\r
126**/\r
127VOID\r
128SaveLocalApicTimerSetting (\r
129 IN CPU_MP_DATA *CpuMpData\r
130 )\r
131{\r
132 //\r
133 // Record the current local APIC timer setting of BSP\r
134 //\r
135 GetApicTimerState (\r
136 &CpuMpData->DivideValue,\r
137 &CpuMpData->PeriodicMode,\r
138 &CpuMpData->Vector\r
139 );\r
140 CpuMpData->CurrentTimerCount = GetApicTimerCurrentCount ();\r
141 CpuMpData->TimerInterruptState = GetApicTimerInterruptState ();\r
142}\r
143\r
144/**\r
145 Sync local APIC timer setting from BSP to AP.\r
146\r
147 @param[in] CpuMpData Pointer to CPU MP Data\r
148**/\r
149VOID\r
150SyncLocalApicTimerSetting (\r
151 IN CPU_MP_DATA *CpuMpData\r
152 )\r
153{\r
154 //\r
155 // Sync local APIC timer setting from BSP to AP\r
156 //\r
157 InitializeApicTimer (\r
158 CpuMpData->DivideValue,\r
159 CpuMpData->CurrentTimerCount,\r
160 CpuMpData->PeriodicMode,\r
161 CpuMpData->Vector\r
162 );\r
163 //\r
164 // Disable AP's local APIC timer interrupt\r
165 //\r
166 DisableApicTimerInterrupt ();\r
167}\r
168\r
169/**\r
170 Save the volatile registers required to be restored following INIT IPI.\r
171\r
172 @param[out] VolatileRegisters Returns buffer saved the volatile resisters\r
173**/\r
174VOID\r
175SaveVolatileRegisters (\r
176 OUT CPU_VOLATILE_REGISTERS *VolatileRegisters\r
177 )\r
178{\r
179 CPUID_VERSION_INFO_EDX VersionInfoEdx;\r
180\r
181 VolatileRegisters->Cr0 = AsmReadCr0 ();\r
182 VolatileRegisters->Cr3 = AsmReadCr3 ();\r
183 VolatileRegisters->Cr4 = AsmReadCr4 ();\r
184\r
185 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32);\r
186 if (VersionInfoEdx.Bits.DE != 0) {\r
187 //\r
188 // If processor supports Debugging Extensions feature\r
189 // by CPUID.[EAX=01H]:EDX.BIT2\r
190 //\r
191 VolatileRegisters->Dr0 = AsmReadDr0 ();\r
192 VolatileRegisters->Dr1 = AsmReadDr1 ();\r
193 VolatileRegisters->Dr2 = AsmReadDr2 ();\r
194 VolatileRegisters->Dr3 = AsmReadDr3 ();\r
195 VolatileRegisters->Dr6 = AsmReadDr6 ();\r
196 VolatileRegisters->Dr7 = AsmReadDr7 ();\r
197 }\r
198\r
199 AsmReadGdtr (&VolatileRegisters->Gdtr);\r
200 AsmReadIdtr (&VolatileRegisters->Idtr);\r
201 VolatileRegisters->Tr = AsmReadTr ();\r
202}\r
203\r
204/**\r
205 Restore the volatile registers following INIT IPI.\r
206\r
207 @param[in] VolatileRegisters Pointer to volatile resisters\r
208 @param[in] IsRestoreDr TRUE: Restore DRx if supported\r
209 FALSE: Do not restore DRx\r
210**/\r
211VOID\r
212RestoreVolatileRegisters (\r
213 IN CPU_VOLATILE_REGISTERS *VolatileRegisters,\r
214 IN BOOLEAN IsRestoreDr\r
215 )\r
216{\r
217 CPUID_VERSION_INFO_EDX VersionInfoEdx;\r
218 IA32_TSS_DESCRIPTOR *Tss;\r
219\r
220 AsmWriteCr3 (VolatileRegisters->Cr3);\r
221 AsmWriteCr4 (VolatileRegisters->Cr4);\r
222 AsmWriteCr0 (VolatileRegisters->Cr0);\r
223\r
224 if (IsRestoreDr) {\r
225 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32);\r
226 if (VersionInfoEdx.Bits.DE != 0) {\r
227 //\r
228 // If processor supports Debugging Extensions feature\r
229 // by CPUID.[EAX=01H]:EDX.BIT2\r
230 //\r
231 AsmWriteDr0 (VolatileRegisters->Dr0);\r
232 AsmWriteDr1 (VolatileRegisters->Dr1);\r
233 AsmWriteDr2 (VolatileRegisters->Dr2);\r
234 AsmWriteDr3 (VolatileRegisters->Dr3);\r
235 AsmWriteDr6 (VolatileRegisters->Dr6);\r
236 AsmWriteDr7 (VolatileRegisters->Dr7);\r
237 }\r
238 }\r
239\r
240 AsmWriteGdtr (&VolatileRegisters->Gdtr);\r
241 AsmWriteIdtr (&VolatileRegisters->Idtr);\r
242 if (VolatileRegisters->Tr != 0 &&\r
243 VolatileRegisters->Tr < VolatileRegisters->Gdtr.Limit) {\r
244 Tss = (IA32_TSS_DESCRIPTOR *)(VolatileRegisters->Gdtr.Base +\r
245 VolatileRegisters->Tr);\r
246 if (Tss->Bits.P == 1) {\r
247 Tss->Bits.Type &= 0xD; // 1101 - Clear busy bit just in case\r
248 AsmWriteTr (VolatileRegisters->Tr);\r
249 }\r
250 }\r
251}\r
252\r
253/**\r
254 Detect whether Mwait-monitor feature is supported.\r
255\r
256 @retval TRUE Mwait-monitor feature is supported.\r
257 @retval FALSE Mwait-monitor feature is not supported.\r
258**/\r
259BOOLEAN\r
260IsMwaitSupport (\r
261 VOID\r
262 )\r
263{\r
264 CPUID_VERSION_INFO_ECX VersionInfoEcx;\r
265\r
266 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, &VersionInfoEcx.Uint32, NULL);\r
267 return (VersionInfoEcx.Bits.MONITOR == 1) ? TRUE : FALSE;\r
268}\r
269\r
270/**\r
271 Get AP loop mode.\r
272\r
273 @param[out] MonitorFilterSize Returns the largest monitor-line size in bytes.\r
274\r
275 @return The AP loop mode.\r
276**/\r
277UINT8\r
278GetApLoopMode (\r
279 OUT UINT32 *MonitorFilterSize\r
280 )\r
281{\r
282 UINT8 ApLoopMode;\r
283 CPUID_MONITOR_MWAIT_EBX MonitorMwaitEbx;\r
284\r
285 ASSERT (MonitorFilterSize != NULL);\r
286\r
287 ApLoopMode = PcdGet8 (PcdCpuApLoopMode);\r
288 ASSERT (ApLoopMode >= ApInHltLoop && ApLoopMode <= ApInRunLoop);\r
289 if (ApLoopMode == ApInMwaitLoop) {\r
290 if (!IsMwaitSupport ()) {\r
291 //\r
292 // If processor does not support MONITOR/MWAIT feature,\r
293 // force AP in Hlt-loop mode\r
294 //\r
295 ApLoopMode = ApInHltLoop;\r
296 }\r
297\r
298 if (PcdGetBool (PcdSevEsIsEnabled)) {\r
299 //\r
300 // For SEV-ES, force AP in Hlt-loop mode in order to use the GHCB\r
301 // protocol for starting APs\r
302 //\r
303 ApLoopMode = ApInHltLoop;\r
304 }\r
305 }\r
306\r
307 if (ApLoopMode != ApInMwaitLoop) {\r
308 *MonitorFilterSize = sizeof (UINT32);\r
309 } else {\r
310 //\r
311 // CPUID.[EAX=05H]:EBX.BIT0-15: Largest monitor-line size in bytes\r
312 // CPUID.[EAX=05H].EDX: C-states supported using MWAIT\r
313 //\r
314 AsmCpuid (CPUID_MONITOR_MWAIT, NULL, &MonitorMwaitEbx.Uint32, NULL, NULL);\r
315 *MonitorFilterSize = MonitorMwaitEbx.Bits.LargestMonitorLineSize;\r
316 }\r
317\r
318 return ApLoopMode;\r
319}\r
320\r
321/**\r
322 Sort the APIC ID of all processors.\r
323\r
324 This function sorts the APIC ID of all processors so that processor number is\r
325 assigned in the ascending order of APIC ID which eases MP debugging.\r
326\r
327 @param[in] CpuMpData Pointer to PEI CPU MP Data\r
328**/\r
329VOID\r
330SortApicId (\r
331 IN CPU_MP_DATA *CpuMpData\r
332 )\r
333{\r
334 UINTN Index1;\r
335 UINTN Index2;\r
336 UINTN Index3;\r
337 UINT32 ApicId;\r
338 CPU_INFO_IN_HOB CpuInfo;\r
339 UINT32 ApCount;\r
340 CPU_INFO_IN_HOB *CpuInfoInHob;\r
341 volatile UINT32 *StartupApSignal;\r
342\r
343 ApCount = CpuMpData->CpuCount - 1;\r
344 CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
345 if (ApCount != 0) {\r
346 for (Index1 = 0; Index1 < ApCount; Index1++) {\r
347 Index3 = Index1;\r
348 //\r
349 // Sort key is the hardware default APIC ID\r
350 //\r
351 ApicId = CpuInfoInHob[Index1].ApicId;\r
352 for (Index2 = Index1 + 1; Index2 <= ApCount; Index2++) {\r
353 if (ApicId > CpuInfoInHob[Index2].ApicId) {\r
354 Index3 = Index2;\r
355 ApicId = CpuInfoInHob[Index2].ApicId;\r
356 }\r
357 }\r
358 if (Index3 != Index1) {\r
359 CopyMem (&CpuInfo, &CpuInfoInHob[Index3], sizeof (CPU_INFO_IN_HOB));\r
360 CopyMem (\r
361 &CpuInfoInHob[Index3],\r
362 &CpuInfoInHob[Index1],\r
363 sizeof (CPU_INFO_IN_HOB)\r
364 );\r
365 CopyMem (&CpuInfoInHob[Index1], &CpuInfo, sizeof (CPU_INFO_IN_HOB));\r
366\r
367 //\r
368 // Also exchange the StartupApSignal.\r
369 //\r
370 StartupApSignal = CpuMpData->CpuData[Index3].StartupApSignal;\r
371 CpuMpData->CpuData[Index3].StartupApSignal =\r
372 CpuMpData->CpuData[Index1].StartupApSignal;\r
373 CpuMpData->CpuData[Index1].StartupApSignal = StartupApSignal;\r
374 }\r
375 }\r
376\r
377 //\r
378 // Get the processor number for the BSP\r
379 //\r
380 ApicId = GetInitialApicId ();\r
381 for (Index1 = 0; Index1 < CpuMpData->CpuCount; Index1++) {\r
382 if (CpuInfoInHob[Index1].ApicId == ApicId) {\r
383 CpuMpData->BspNumber = (UINT32) Index1;\r
384 break;\r
385 }\r
386 }\r
387 }\r
388}\r
389\r
390/**\r
391 Enable x2APIC mode on APs.\r
392\r
393 @param[in, out] Buffer Pointer to private data buffer.\r
394**/\r
395VOID\r
396EFIAPI\r
397ApFuncEnableX2Apic (\r
398 IN OUT VOID *Buffer\r
399 )\r
400{\r
401 SetApicMode (LOCAL_APIC_MODE_X2APIC);\r
402}\r
403\r
404/**\r
405 Do sync on APs.\r
406\r
407 @param[in, out] Buffer Pointer to private data buffer.\r
408**/\r
409VOID\r
410EFIAPI\r
411ApInitializeSync (\r
412 IN OUT VOID *Buffer\r
413 )\r
414{\r
415 CPU_MP_DATA *CpuMpData;\r
416 UINTN ProcessorNumber;\r
417 EFI_STATUS Status;\r
418\r
419 CpuMpData = (CPU_MP_DATA *) Buffer;\r
420 Status = GetProcessorNumber (CpuMpData, &ProcessorNumber);\r
421 ASSERT_EFI_ERROR (Status);\r
422 //\r
423 // Load microcode on AP\r
424 //\r
425 MicrocodeDetect (CpuMpData, ProcessorNumber);\r
426 //\r
427 // Sync BSP's MTRR table to AP\r
428 //\r
429 MtrrSetAllMtrrs (&CpuMpData->MtrrTable);\r
430}\r
431\r
432/**\r
433 Find the current Processor number by APIC ID.\r
434\r
435 @param[in] CpuMpData Pointer to PEI CPU MP Data\r
436 @param[out] ProcessorNumber Return the pocessor number found\r
437\r
438 @retval EFI_SUCCESS ProcessorNumber is found and returned.\r
439 @retval EFI_NOT_FOUND ProcessorNumber is not found.\r
440**/\r
441EFI_STATUS\r
442GetProcessorNumber (\r
443 IN CPU_MP_DATA *CpuMpData,\r
444 OUT UINTN *ProcessorNumber\r
445 )\r
446{\r
447 UINTN TotalProcessorNumber;\r
448 UINTN Index;\r
449 CPU_INFO_IN_HOB *CpuInfoInHob;\r
450 UINT32 CurrentApicId;\r
451\r
452 CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
453\r
454 TotalProcessorNumber = CpuMpData->CpuCount;\r
455 CurrentApicId = GetApicId ();\r
456 for (Index = 0; Index < TotalProcessorNumber; Index ++) {\r
457 if (CpuInfoInHob[Index].ApicId == CurrentApicId) {\r
458 *ProcessorNumber = Index;\r
459 return EFI_SUCCESS;\r
460 }\r
461 }\r
462\r
463 return EFI_NOT_FOUND;\r
464}\r
465\r
466/**\r
467 This function will get CPU count in the system.\r
468\r
469 @param[in] CpuMpData Pointer to PEI CPU MP Data\r
470\r
471 @return CPU count detected\r
472**/\r
473UINTN\r
474CollectProcessorCount (\r
475 IN CPU_MP_DATA *CpuMpData\r
476 )\r
477{\r
478 UINTN Index;\r
479 CPU_INFO_IN_HOB *CpuInfoInHob;\r
480 BOOLEAN X2Apic;\r
481\r
482 //\r
483 // Send 1st broadcast IPI to APs to wakeup APs\r
484 //\r
485 CpuMpData->InitFlag = ApInitConfig;\r
486 WakeUpAP (CpuMpData, TRUE, 0, NULL, NULL, TRUE);\r
487 CpuMpData->InitFlag = ApInitDone;\r
488 ASSERT (CpuMpData->CpuCount <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber));\r
489 //\r
490 // Wait for all APs finished the initialization\r
491 //\r
492 while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) {\r
493 CpuPause ();\r
494 }\r
495\r
496\r
497 //\r
498 // Enable x2APIC mode if\r
499 // 1. Number of CPU is greater than 255; or\r
500 // 2. There are any logical processors reporting an Initial APIC ID of 255 or greater.\r
501 //\r
502 X2Apic = FALSE;\r
503 if (CpuMpData->CpuCount > 255) {\r
504 //\r
505 // If there are more than 255 processor found, force to enable X2APIC\r
506 //\r
507 X2Apic = TRUE;\r
508 } else {\r
509 CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
510 for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
511 if (CpuInfoInHob[Index].InitialApicId >= 0xFF) {\r
512 X2Apic = TRUE;\r
513 break;\r
514 }\r
515 }\r
516 }\r
517\r
518 if (X2Apic) {\r
519 DEBUG ((DEBUG_INFO, "Force x2APIC mode!\n"));\r
520 //\r
521 // Wakeup all APs to enable x2APIC mode\r
522 //\r
523 WakeUpAP (CpuMpData, TRUE, 0, ApFuncEnableX2Apic, NULL, TRUE);\r
524 //\r
525 // Wait for all known APs finished\r
526 //\r
527 while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) {\r
528 CpuPause ();\r
529 }\r
530 //\r
531 // Enable x2APIC on BSP\r
532 //\r
533 SetApicMode (LOCAL_APIC_MODE_X2APIC);\r
534 //\r
535 // Set BSP/Aps state to IDLE\r
536 //\r
537 for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
538 SetApState (&CpuMpData->CpuData[Index], CpuStateIdle);\r
539 }\r
540 }\r
541 DEBUG ((DEBUG_INFO, "APIC MODE is %d\n", GetApicMode ()));\r
542 //\r
543 // Sort BSP/Aps by CPU APIC ID in ascending order\r
544 //\r
545 SortApicId (CpuMpData);\r
546\r
547 DEBUG ((DEBUG_INFO, "MpInitLib: Find %d processors in system.\n", CpuMpData->CpuCount));\r
548\r
549 return CpuMpData->CpuCount;\r
550}\r
551\r
552/**\r
553 Initialize CPU AP Data when AP is wakeup at the first time.\r
554\r
555 @param[in, out] CpuMpData Pointer to PEI CPU MP Data\r
556 @param[in] ProcessorNumber The handle number of processor\r
557 @param[in] BistData Processor BIST data\r
558 @param[in] ApTopOfStack Top of AP stack\r
559\r
560**/\r
561VOID\r
562InitializeApData (\r
563 IN OUT CPU_MP_DATA *CpuMpData,\r
564 IN UINTN ProcessorNumber,\r
565 IN UINT32 BistData,\r
566 IN UINT64 ApTopOfStack\r
567 )\r
568{\r
569 CPU_INFO_IN_HOB *CpuInfoInHob;\r
570 MSR_IA32_PLATFORM_ID_REGISTER PlatformIdMsr;\r
571\r
572 CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
573 CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId ();\r
574 CpuInfoInHob[ProcessorNumber].ApicId = GetApicId ();\r
575 CpuInfoInHob[ProcessorNumber].Health = BistData;\r
576 CpuInfoInHob[ProcessorNumber].ApTopOfStack = ApTopOfStack;\r
577\r
578 CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE;\r
579 CpuMpData->CpuData[ProcessorNumber].CpuHealthy = (BistData == 0) ? TRUE : FALSE;\r
580\r
581 //\r
582 // NOTE: PlatformId is not relevant on AMD platforms.\r
583 //\r
584 if (!StandardSignatureIsAuthenticAMD ()) {\r
585 PlatformIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID);\r
586 CpuMpData->CpuData[ProcessorNumber].PlatformId = (UINT8)PlatformIdMsr.Bits.PlatformId;\r
587 }\r
588\r
589 AsmCpuid (\r
590 CPUID_VERSION_INFO,\r
591 &CpuMpData->CpuData[ProcessorNumber].ProcessorSignature,\r
592 NULL,\r
593 NULL,\r
594 NULL\r
595 );\r
596\r
597 InitializeSpinLock(&CpuMpData->CpuData[ProcessorNumber].ApLock);\r
598 SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);\r
599}\r
600\r
601/**\r
602 Get Protected mode code segment with 16-bit default addressing\r
603 from current GDT table.\r
604\r
605 @return Protected mode 16-bit code segment value.\r
606**/\r
607STATIC\r
608UINT16\r
609GetProtectedMode16CS (\r
610 VOID\r
611 )\r
612{\r
613 IA32_DESCRIPTOR GdtrDesc;\r
614 IA32_SEGMENT_DESCRIPTOR *GdtEntry;\r
615 UINTN GdtEntryCount;\r
616 UINT16 Index;\r
617\r
618 Index = (UINT16) -1;\r
619 AsmReadGdtr (&GdtrDesc);\r
620 GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
621 GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;\r
622 for (Index = 0; Index < GdtEntryCount; Index++) {\r
623 if (GdtEntry->Bits.L == 0 &&\r
624 GdtEntry->Bits.DB == 0 &&\r
625 GdtEntry->Bits.Type > 8) {\r
626 break;\r
627 }\r
628 GdtEntry++;\r
629 }\r
630 ASSERT (Index != GdtEntryCount);\r
631 return Index * 8;\r
632}\r
633\r
634/**\r
635 Get Protected mode code segment with 32-bit default addressing\r
636 from current GDT table.\r
637\r
638 @return Protected mode 32-bit code segment value.\r
639**/\r
640STATIC\r
641UINT16\r
642GetProtectedMode32CS (\r
643 VOID\r
644 )\r
645{\r
646 IA32_DESCRIPTOR GdtrDesc;\r
647 IA32_SEGMENT_DESCRIPTOR *GdtEntry;\r
648 UINTN GdtEntryCount;\r
649 UINT16 Index;\r
650\r
651 Index = (UINT16) -1;\r
652 AsmReadGdtr (&GdtrDesc);\r
653 GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
654 GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;\r
655 for (Index = 0; Index < GdtEntryCount; Index++) {\r
656 if (GdtEntry->Bits.L == 0 &&\r
657 GdtEntry->Bits.DB == 1 &&\r
658 GdtEntry->Bits.Type > 8) {\r
659 break;\r
660 }\r
661 GdtEntry++;\r
662 }\r
663 ASSERT (Index != GdtEntryCount);\r
664 return Index * 8;\r
665}\r
666\r
667/**\r
668 Reset an AP when in SEV-ES mode.\r
669\r
670 If successful, this function never returns.\r
671\r
672 @param[in] Ghcb Pointer to the GHCB\r
673 @param[in] CpuMpData Pointer to CPU MP Data\r
674\r
675**/\r
676STATIC\r
677VOID\r
678MpInitLibSevEsAPReset (\r
679 IN GHCB *Ghcb,\r
680 IN CPU_MP_DATA *CpuMpData\r
681 )\r
682{\r
683 UINT16 Code16, Code32;\r
684 AP_RESET *APResetFn;\r
685 UINTN BufferStart;\r
686 UINTN StackStart;\r
687\r
688 Code16 = GetProtectedMode16CS ();\r
689 Code32 = GetProtectedMode32CS ();\r
690\r
691 if (CpuMpData->WakeupBufferHigh != 0) {\r
692 APResetFn = (AP_RESET *) (CpuMpData->WakeupBufferHigh + CpuMpData->AddressMap.SwitchToRealNoNxOffset);\r
693 } else {\r
694 APResetFn = (AP_RESET *) (CpuMpData->MpCpuExchangeInfo->BufferStart + CpuMpData->AddressMap.SwitchToRealOffset);\r
695 }\r
696\r
697 BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;\r
698 StackStart = CpuMpData->SevEsAPResetStackStart -\r
699 (AP_RESET_STACK_SIZE * GetApicId ());\r
700\r
701 //\r
702 // This call never returns.\r
703 //\r
704 APResetFn (BufferStart, Code16, Code32, StackStart);\r
705}\r
706\r
707/**\r
708 This function will be called from AP reset code if BSP uses WakeUpAP.\r
709\r
710 @param[in] ExchangeInfo Pointer to the MP exchange info buffer\r
711 @param[in] ApIndex Number of current executing AP\r
712**/\r
713VOID\r
714EFIAPI\r
715ApWakeupFunction (\r
716 IN MP_CPU_EXCHANGE_INFO *ExchangeInfo,\r
717 IN UINTN ApIndex\r
718 )\r
719{\r
720 CPU_MP_DATA *CpuMpData;\r
721 UINTN ProcessorNumber;\r
722 EFI_AP_PROCEDURE Procedure;\r
723 VOID *Parameter;\r
724 UINT32 BistData;\r
725 volatile UINT32 *ApStartupSignalBuffer;\r
726 CPU_INFO_IN_HOB *CpuInfoInHob;\r
727 UINT64 ApTopOfStack;\r
728 UINTN CurrentApicMode;\r
729\r
730 //\r
731 // AP finished assembly code and begin to execute C code\r
732 //\r
733 CpuMpData = ExchangeInfo->CpuMpData;\r
734\r
735 //\r
736 // AP's local APIC settings will be lost after received INIT IPI\r
737 // We need to re-initialize them at here\r
738 //\r
739 ProgramVirtualWireMode ();\r
740 //\r
741 // Mask the LINT0 and LINT1 so that AP doesn't enter the system timer interrupt handler.\r
742 //\r
743 DisableLvtInterrupts ();\r
744 SyncLocalApicTimerSetting (CpuMpData);\r
745\r
746 CurrentApicMode = GetApicMode ();\r
747 while (TRUE) {\r
748 if (CpuMpData->InitFlag == ApInitConfig) {\r
749 //\r
750 // Add CPU number\r
751 //\r
752 InterlockedIncrement ((UINT32 *) &CpuMpData->CpuCount);\r
753 ProcessorNumber = ApIndex;\r
754 //\r
755 // This is first time AP wakeup, get BIST information from AP stack\r
756 //\r
757 ApTopOfStack = CpuMpData->Buffer + (ProcessorNumber + 1) * CpuMpData->CpuApStackSize;\r
758 BistData = *(UINT32 *) ((UINTN) ApTopOfStack - sizeof (UINTN));\r
759 //\r
760 // CpuMpData->CpuData[0].VolatileRegisters is initialized based on BSP environment,\r
761 // to initialize AP in InitConfig path.\r
762 // NOTE: IDTR.BASE stored in CpuMpData->CpuData[0].VolatileRegisters points to a different IDT shared by all APs.\r
763 //\r
764 RestoreVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters, FALSE);\r
765 InitializeApData (CpuMpData, ProcessorNumber, BistData, ApTopOfStack);\r
766 ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal;\r
767\r
768 //\r
769 // Delay decrementing the APs executing count when SEV-ES is enabled\r
770 // to allow the APs to issue an AP_RESET_HOLD before the BSP possibly\r
771 // performs another INIT-SIPI-SIPI sequence.\r
772 //\r
773 if (!CpuMpData->SevEsIsEnabled) {\r
774 InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);\r
775 }\r
776 } else {\r
777 //\r
778 // Execute AP function if AP is ready\r
779 //\r
780 GetProcessorNumber (CpuMpData, &ProcessorNumber);\r
781 //\r
782 // Clear AP start-up signal when AP waken up\r
783 //\r
784 ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal;\r
785 InterlockedCompareExchange32 (\r
786 (UINT32 *) ApStartupSignalBuffer,\r
787 WAKEUP_AP_SIGNAL,\r
788 0\r
789 );\r
790\r
791 if (CpuMpData->InitFlag == ApInitReconfig) {\r
792 //\r
793 // ApInitReconfig happens when:\r
794 // 1. AP is re-enabled after it's disabled, in either PEI or DXE phase.\r
795 // 2. AP is initialized in DXE phase.\r
796 // In either case, use the volatile registers value derived from BSP.\r
797 // NOTE: IDTR.BASE stored in CpuMpData->CpuData[0].VolatileRegisters points to a\r
798 // different IDT shared by all APs.\r
799 //\r
800 RestoreVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters, FALSE);\r
801 } else {\r
802 if (CpuMpData->ApLoopMode == ApInHltLoop) {\r
803 //\r
804 // Restore AP's volatile registers saved before AP is halted\r
805 //\r
806 RestoreVolatileRegisters (&CpuMpData->CpuData[ProcessorNumber].VolatileRegisters, TRUE);\r
807 } else {\r
808 //\r
809 // The CPU driver might not flush TLB for APs on spot after updating\r
810 // page attributes. AP in mwait loop mode needs to take care of it when\r
811 // woken up.\r
812 //\r
813 CpuFlushTlb ();\r
814 }\r
815 }\r
816\r
817 if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateReady) {\r
818 Procedure = (EFI_AP_PROCEDURE)CpuMpData->CpuData[ProcessorNumber].ApFunction;\r
819 Parameter = (VOID *) CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument;\r
820 if (Procedure != NULL) {\r
821 SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateBusy);\r
822 //\r
823 // Enable source debugging on AP function\r
824 //\r
825 EnableDebugAgent ();\r
826 //\r
827 // Invoke AP function here\r
828 //\r
829 Procedure (Parameter);\r
830 CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
831 if (CpuMpData->SwitchBspFlag) {\r
832 //\r
833 // Re-get the processor number due to BSP/AP maybe exchange in AP function\r
834 //\r
835 GetProcessorNumber (CpuMpData, &ProcessorNumber);\r
836 CpuMpData->CpuData[ProcessorNumber].ApFunction = 0;\r
837 CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument = 0;\r
838 ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal;\r
839 CpuInfoInHob[ProcessorNumber].ApTopOfStack = CpuInfoInHob[CpuMpData->NewBspNumber].ApTopOfStack;\r
840 } else {\r
841 if (CpuInfoInHob[ProcessorNumber].ApicId != GetApicId () ||\r
842 CpuInfoInHob[ProcessorNumber].InitialApicId != GetInitialApicId ()) {\r
843 if (CurrentApicMode != GetApicMode ()) {\r
844 //\r
845 // If APIC mode change happened during AP function execution,\r
846 // we do not support APIC ID value changed.\r
847 //\r
848 ASSERT (FALSE);\r
849 CpuDeadLoop ();\r
850 } else {\r
851 //\r
852 // Re-get the CPU APICID and Initial APICID if they are changed\r
853 //\r
854 CpuInfoInHob[ProcessorNumber].ApicId = GetApicId ();\r
855 CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId ();\r
856 }\r
857 }\r
858 }\r
859 }\r
860 SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateFinished);\r
861 }\r
862 }\r
863\r
864 //\r
865 // AP finished executing C code\r
866 //\r
867 InterlockedIncrement ((UINT32 *) &CpuMpData->FinishedCount);\r
868\r
869 //\r
870 // Place AP is specified loop mode\r
871 //\r
872 if (CpuMpData->ApLoopMode == ApInHltLoop) {\r
873 //\r
874 // Save AP volatile registers\r
875 //\r
876 SaveVolatileRegisters (&CpuMpData->CpuData[ProcessorNumber].VolatileRegisters);\r
877 //\r
878 // Place AP in HLT-loop\r
879 //\r
880 while (TRUE) {\r
881 DisableInterrupts ();\r
882 if (CpuMpData->SevEsIsEnabled) {\r
883 MSR_SEV_ES_GHCB_REGISTER Msr;\r
884 GHCB *Ghcb;\r
885 UINT64 Status;\r
886 BOOLEAN DoDecrement;\r
887\r
888 if (CpuMpData->InitFlag == ApInitConfig) {\r
889 DoDecrement = TRUE;\r
890 }\r
891\r
892 while (TRUE) {\r
893 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
894 Ghcb = Msr.Ghcb;\r
895\r
896 VmgInit (Ghcb);\r
897\r
898 if (DoDecrement) {\r
899 DoDecrement = FALSE;\r
900\r
901 //\r
902 // Perform the delayed decrement just before issuing the first\r
903 // VMGEXIT with AP_RESET_HOLD.\r
904 //\r
905 InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);\r
906 }\r
907\r
908 Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);\r
909 if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {\r
910 VmgDone (Ghcb);\r
911 break;\r
912 }\r
913\r
914 VmgDone (Ghcb);\r
915 }\r
916\r
917 //\r
918 // Awakened in a new phase? Use the new CpuMpData\r
919 //\r
920 if (CpuMpData->NewCpuMpData != NULL) {\r
921 CpuMpData = CpuMpData->NewCpuMpData;\r
922 }\r
923\r
924 MpInitLibSevEsAPReset (Ghcb, CpuMpData);\r
925 } else {\r
926 CpuSleep ();\r
927 }\r
928 CpuPause ();\r
929 }\r
930 }\r
931 while (TRUE) {\r
932 DisableInterrupts ();\r
933 if (CpuMpData->ApLoopMode == ApInMwaitLoop) {\r
934 //\r
935 // Place AP in MWAIT-loop\r
936 //\r
937 AsmMonitor ((UINTN) ApStartupSignalBuffer, 0, 0);\r
938 if (*ApStartupSignalBuffer != WAKEUP_AP_SIGNAL) {\r
939 //\r
940 // Check AP start-up signal again.\r
941 // If AP start-up signal is not set, place AP into\r
942 // the specified C-state\r
943 //\r
944 AsmMwait (CpuMpData->ApTargetCState << 4, 0);\r
945 }\r
946 } else if (CpuMpData->ApLoopMode == ApInRunLoop) {\r
947 //\r
948 // Place AP in Run-loop\r
949 //\r
950 CpuPause ();\r
951 } else {\r
952 ASSERT (FALSE);\r
953 }\r
954\r
955 //\r
956 // If AP start-up signal is written, AP is waken up\r
957 // otherwise place AP in loop again\r
958 //\r
959 if (*ApStartupSignalBuffer == WAKEUP_AP_SIGNAL) {\r
960 break;\r
961 }\r
962 }\r
963 }\r
964}\r
965\r
966/**\r
967 Wait for AP wakeup and write AP start-up signal till AP is waken up.\r
968\r
969 @param[in] ApStartupSignalBuffer Pointer to AP wakeup signal\r
970**/\r
971VOID\r
972WaitApWakeup (\r
973 IN volatile UINT32 *ApStartupSignalBuffer\r
974 )\r
975{\r
976 //\r
977 // If AP is waken up, StartupApSignal should be cleared.\r
978 // Otherwise, write StartupApSignal again till AP waken up.\r
979 //\r
980 while (InterlockedCompareExchange32 (\r
981 (UINT32 *) ApStartupSignalBuffer,\r
982 WAKEUP_AP_SIGNAL,\r
983 WAKEUP_AP_SIGNAL\r
984 ) != 0) {\r
985 CpuPause ();\r
986 }\r
987}\r
988\r
989/**\r
990 This function will fill the exchange info structure.\r
991\r
992 @param[in] CpuMpData Pointer to CPU MP Data\r
993\r
994**/\r
995VOID\r
996FillExchangeInfoData (\r
997 IN CPU_MP_DATA *CpuMpData\r
998 )\r
999{\r
1000 volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;\r
1001 UINTN Size;\r
1002 IA32_SEGMENT_DESCRIPTOR *Selector;\r
1003 IA32_CR4 Cr4;\r
1004\r
1005 ExchangeInfo = CpuMpData->MpCpuExchangeInfo;\r
1006 ExchangeInfo->Lock = 0;\r
1007 ExchangeInfo->StackStart = CpuMpData->Buffer;\r
1008 ExchangeInfo->StackSize = CpuMpData->CpuApStackSize;\r
1009 ExchangeInfo->BufferStart = CpuMpData->WakeupBuffer;\r
1010 ExchangeInfo->ModeOffset = CpuMpData->AddressMap.ModeEntryOffset;\r
1011\r
1012 ExchangeInfo->CodeSegment = AsmReadCs ();\r
1013 ExchangeInfo->DataSegment = AsmReadDs ();\r
1014\r
1015 ExchangeInfo->Cr3 = AsmReadCr3 ();\r
1016\r
1017 ExchangeInfo->CFunction = (UINTN) ApWakeupFunction;\r
1018 ExchangeInfo->ApIndex = 0;\r
1019 ExchangeInfo->NumApsExecuting = 0;\r
1020 ExchangeInfo->InitFlag = (UINTN) CpuMpData->InitFlag;\r
1021 ExchangeInfo->CpuInfo = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
1022 ExchangeInfo->CpuMpData = CpuMpData;\r
1023\r
1024 ExchangeInfo->EnableExecuteDisable = IsBspExecuteDisableEnabled ();\r
1025\r
1026 ExchangeInfo->InitializeFloatingPointUnitsAddress = (UINTN)InitializeFloatingPointUnits;\r
1027\r
1028 //\r
1029 // We can check either CPUID(7).ECX[bit16] or check CR4.LA57[bit12]\r
1030 // to determin whether 5-Level Paging is enabled.\r
1031 // CPUID(7).ECX[bit16] shows CPU's capability, CR4.LA57[bit12] shows\r
1032 // current system setting.\r
1033 // Using latter way is simpler because it also eliminates the needs to\r
1034 // check whether platform wants to enable it.\r
1035 //\r
1036 Cr4.UintN = AsmReadCr4 ();\r
1037 ExchangeInfo->Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);\r
1038 DEBUG ((DEBUG_INFO, "%a: 5-Level Paging = %d\n", gEfiCallerBaseName, ExchangeInfo->Enable5LevelPaging));\r
1039\r
1040 ExchangeInfo->SevEsIsEnabled = CpuMpData->SevEsIsEnabled;\r
1041 ExchangeInfo->GhcbBase = (UINTN) CpuMpData->GhcbBase;\r
1042\r
1043 //\r
1044 // Get the BSP's data of GDT and IDT\r
1045 //\r
1046 AsmReadGdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->GdtrProfile);\r
1047 AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile);\r
1048\r
1049 //\r
1050 // Find a 32-bit code segment\r
1051 //\r
1052 Selector = (IA32_SEGMENT_DESCRIPTOR *)ExchangeInfo->GdtrProfile.Base;\r
1053 Size = ExchangeInfo->GdtrProfile.Limit + 1;\r
1054 while (Size > 0) {\r
1055 if (Selector->Bits.L == 0 && Selector->Bits.Type >= 8) {\r
1056 ExchangeInfo->ModeTransitionSegment =\r
1057 (UINT16)((UINTN)Selector - ExchangeInfo->GdtrProfile.Base);\r
1058 break;\r
1059 }\r
1060 Selector += 1;\r
1061 Size -= sizeof (IA32_SEGMENT_DESCRIPTOR);\r
1062 }\r
1063\r
1064 //\r
1065 // Copy all 32-bit code and 64-bit code into memory with type of\r
1066 // EfiBootServicesCode to avoid page fault if NX memory protection is enabled.\r
1067 //\r
1068 if (CpuMpData->WakeupBufferHigh != 0) {\r
1069 Size = CpuMpData->AddressMap.RendezvousFunnelSize +\r
1070 CpuMpData->AddressMap.SwitchToRealSize -\r
1071 CpuMpData->AddressMap.ModeTransitionOffset;\r
1072 CopyMem (\r
1073 (VOID *)CpuMpData->WakeupBufferHigh,\r
1074 CpuMpData->AddressMap.RendezvousFunnelAddress +\r
1075 CpuMpData->AddressMap.ModeTransitionOffset,\r
1076 Size\r
1077 );\r
1078\r
1079 ExchangeInfo->ModeTransitionMemory = (UINT32)CpuMpData->WakeupBufferHigh;\r
1080 } else {\r
1081 ExchangeInfo->ModeTransitionMemory = (UINT32)\r
1082 (ExchangeInfo->BufferStart + CpuMpData->AddressMap.ModeTransitionOffset);\r
1083 }\r
1084\r
1085 ExchangeInfo->ModeHighMemory = ExchangeInfo->ModeTransitionMemory +\r
1086 (UINT32)ExchangeInfo->ModeOffset -\r
1087 (UINT32)CpuMpData->AddressMap.ModeTransitionOffset;\r
1088 ExchangeInfo->ModeHighSegment = (UINT16)ExchangeInfo->CodeSegment;\r
1089}\r
1090\r
1091/**\r
1092 Helper function that waits until the finished AP count reaches the specified\r
1093 limit, or the specified timeout elapses (whichever comes first).\r
1094\r
1095 @param[in] CpuMpData Pointer to CPU MP Data.\r
1096 @param[in] FinishedApLimit The number of finished APs to wait for.\r
1097 @param[in] TimeLimit The number of microseconds to wait for.\r
1098**/\r
1099VOID\r
1100TimedWaitForApFinish (\r
1101 IN CPU_MP_DATA *CpuMpData,\r
1102 IN UINT32 FinishedApLimit,\r
1103 IN UINT32 TimeLimit\r
1104 );\r
1105\r
1106/**\r
1107 Get available system memory below 1MB by specified size.\r
1108\r
1109 @param[in] CpuMpData The pointer to CPU MP Data structure.\r
1110**/\r
1111VOID\r
1112BackupAndPrepareWakeupBuffer(\r
1113 IN CPU_MP_DATA *CpuMpData\r
1114 )\r
1115{\r
1116 CopyMem (\r
1117 (VOID *) CpuMpData->BackupBuffer,\r
1118 (VOID *) CpuMpData->WakeupBuffer,\r
1119 CpuMpData->BackupBufferSize\r
1120 );\r
1121 CopyMem (\r
1122 (VOID *) CpuMpData->WakeupBuffer,\r
1123 (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,\r
1124 CpuMpData->AddressMap.RendezvousFunnelSize +\r
1125 CpuMpData->AddressMap.SwitchToRealSize\r
1126 );\r
1127}\r
1128\r
1129/**\r
1130 Restore wakeup buffer data.\r
1131\r
1132 @param[in] CpuMpData The pointer to CPU MP Data structure.\r
1133**/\r
1134VOID\r
1135RestoreWakeupBuffer(\r
1136 IN CPU_MP_DATA *CpuMpData\r
1137 )\r
1138{\r
1139 CopyMem (\r
1140 (VOID *) CpuMpData->WakeupBuffer,\r
1141 (VOID *) CpuMpData->BackupBuffer,\r
1142 CpuMpData->BackupBufferSize\r
1143 );\r
1144}\r
1145\r
1146/**\r
1147 Calculate the size of the reset stack.\r
1148\r
1149 @return Total amount of memory required for stacks\r
1150**/\r
1151STATIC\r
1152UINTN\r
1153GetApResetStackSize (\r
1154 VOID\r
1155 )\r
1156{\r
1157 return AP_RESET_STACK_SIZE * PcdGet32(PcdCpuMaxLogicalProcessorNumber);\r
1158}\r
1159\r
1160/**\r
1161 Calculate the size of the reset vector.\r
1162\r
1163 @param[in] AddressMap The pointer to Address Map structure.\r
1164\r
1165 @return Total amount of memory required for the AP reset area\r
1166**/\r
1167STATIC\r
1168UINTN\r
1169GetApResetVectorSize (\r
1170 IN MP_ASSEMBLY_ADDRESS_MAP *AddressMap\r
1171 )\r
1172{\r
1173 UINTN Size;\r
1174\r
1175 Size = ALIGN_VALUE (AddressMap->RendezvousFunnelSize +\r
1176 AddressMap->SwitchToRealSize +\r
1177 sizeof (MP_CPU_EXCHANGE_INFO),\r
1178 CPU_STACK_ALIGNMENT);\r
1179 Size += GetApResetStackSize ();\r
1180\r
1181 return Size;\r
1182}\r
1183\r
1184/**\r
1185 Allocate reset vector buffer.\r
1186\r
1187 @param[in, out] CpuMpData The pointer to CPU MP Data structure.\r
1188**/\r
1189VOID\r
1190AllocateResetVector (\r
1191 IN OUT CPU_MP_DATA *CpuMpData\r
1192 )\r
1193{\r
1194 UINTN ApResetVectorSize;\r
1195\r
1196 if (CpuMpData->WakeupBuffer == (UINTN) -1) {\r
1197 ApResetVectorSize = GetApResetVectorSize (&CpuMpData->AddressMap);\r
1198\r
1199 CpuMpData->WakeupBuffer = GetWakeupBuffer (ApResetVectorSize);\r
1200 CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)\r
1201 (CpuMpData->WakeupBuffer +\r
1202 CpuMpData->AddressMap.RendezvousFunnelSize +\r
1203 CpuMpData->AddressMap.SwitchToRealSize);\r
1204 CpuMpData->WakeupBufferHigh = GetModeTransitionBuffer (\r
1205 CpuMpData->AddressMap.RendezvousFunnelSize +\r
1206 CpuMpData->AddressMap.SwitchToRealSize -\r
1207 CpuMpData->AddressMap.ModeTransitionOffset\r
1208 );\r
1209 //\r
1210 // The reset stack starts at the end of the buffer.\r
1211 //\r
1212 CpuMpData->SevEsAPResetStackStart = CpuMpData->WakeupBuffer + ApResetVectorSize;\r
1213 }\r
1214 BackupAndPrepareWakeupBuffer (CpuMpData);\r
1215}\r
1216\r
1217/**\r
1218 Free AP reset vector buffer.\r
1219\r
1220 @param[in] CpuMpData The pointer to CPU MP Data structure.\r
1221**/\r
1222VOID\r
1223FreeResetVector (\r
1224 IN CPU_MP_DATA *CpuMpData\r
1225 )\r
1226{\r
1227 //\r
1228 // If SEV-ES is enabled, the reset area is needed for AP parking and\r
1229 // and AP startup in the OS, so the reset area is reserved. Do not\r
1230 // perform the restore as this will overwrite memory which has data\r
1231 // needed by SEV-ES.\r
1232 //\r
1233 if (!CpuMpData->SevEsIsEnabled) {\r
1234 RestoreWakeupBuffer (CpuMpData);\r
1235 }\r
1236}\r
1237\r
1238/**\r
1239 Allocate the SEV-ES AP jump table buffer.\r
1240\r
1241 @param[in, out] CpuMpData The pointer to CPU MP Data structure.\r
1242**/\r
1243VOID\r
1244AllocateSevEsAPMemory (\r
1245 IN OUT CPU_MP_DATA *CpuMpData\r
1246 )\r
1247{\r
1248 if (CpuMpData->SevEsAPBuffer == (UINTN) -1) {\r
1249 CpuMpData->SevEsAPBuffer =\r
1250 CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;\r
1251 }\r
1252}\r
1253\r
1254/**\r
1255 Program the SEV-ES AP jump table buffer.\r
1256\r
1257 @param[in] SipiVector The SIPI vector used for the AP Reset\r
1258**/\r
1259VOID\r
1260SetSevEsJumpTable (\r
1261 IN UINTN SipiVector\r
1262 )\r
1263{\r
1264 SEV_ES_AP_JMP_FAR *JmpFar;\r
1265 UINT32 Offset, InsnByte;\r
1266 UINT8 LoNib, HiNib;\r
1267\r
1268 JmpFar = (SEV_ES_AP_JMP_FAR *) FixedPcdGet32 (PcdSevEsWorkAreaBase);\r
1269 ASSERT (JmpFar != NULL);\r
1270\r
1271 //\r
1272 // Obtain the address of the Segment/Rip location in the workarea.\r
1273 // This will be set to a value derived from the SIPI vector and will\r
1274 // be the memory address used for the far jump below.\r
1275 //\r
1276 Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);\r
1277 Offset += sizeof (JmpFar->InsnBuffer);\r
1278 LoNib = (UINT8) Offset;\r
1279 HiNib = (UINT8) (Offset >> 8);\r
1280\r
1281 //\r
1282 // Program the workarea (which is the initial AP boot address) with\r
1283 // far jump to the SIPI vector (where XX and YY represent the\r
1284 // address of where the SIPI vector is stored.\r
1285 //\r
1286 // JMP FAR [CS:XXYY] => 2E FF 2E YY XX\r
1287 //\r
1288 InsnByte = 0;\r
1289 JmpFar->InsnBuffer[InsnByte++] = 0x2E; // CS override prefix\r
1290 JmpFar->InsnBuffer[InsnByte++] = 0xFF; // JMP (FAR)\r
1291 JmpFar->InsnBuffer[InsnByte++] = 0x2E; // ModRM (JMP memory location)\r
1292 JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ...\r
1293 JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ...\r
1294\r
1295 //\r
1296 // Program the Segment/Rip based on the SIPI vector (always at least\r
1297 // 16-byte aligned, so Rip is set to 0).\r
1298 //\r
1299 JmpFar->Rip = 0;\r
1300 JmpFar->Segment = (UINT16) (SipiVector >> 4);\r
1301}\r
1302\r
1303/**\r
1304 This function will be called by BSP to wakeup AP.\r
1305\r
1306 @param[in] CpuMpData Pointer to CPU MP Data\r
1307 @param[in] Broadcast TRUE: Send broadcast IPI to all APs\r
1308 FALSE: Send IPI to AP by ApicId\r
1309 @param[in] ProcessorNumber The handle number of specified processor\r
1310 @param[in] Procedure The function to be invoked by AP\r
1311 @param[in] ProcedureArgument The argument to be passed into AP function\r
1312 @param[in] WakeUpDisabledAps Whether need to wake up disabled APs in broadcast mode.\r
1313**/\r
1314VOID\r
1315WakeUpAP (\r
1316 IN CPU_MP_DATA *CpuMpData,\r
1317 IN BOOLEAN Broadcast,\r
1318 IN UINTN ProcessorNumber,\r
1319 IN EFI_AP_PROCEDURE Procedure, OPTIONAL\r
1320 IN VOID *ProcedureArgument, OPTIONAL\r
1321 IN BOOLEAN WakeUpDisabledAps\r
1322 )\r
1323{\r
1324 volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;\r
1325 UINTN Index;\r
1326 CPU_AP_DATA *CpuData;\r
1327 BOOLEAN ResetVectorRequired;\r
1328 CPU_INFO_IN_HOB *CpuInfoInHob;\r
1329\r
1330 CpuMpData->FinishedCount = 0;\r
1331 ResetVectorRequired = FALSE;\r
1332\r
1333 if (CpuMpData->WakeUpByInitSipiSipi ||\r
1334 CpuMpData->InitFlag != ApInitDone) {\r
1335 ResetVectorRequired = TRUE;\r
1336 AllocateResetVector (CpuMpData);\r
1337 AllocateSevEsAPMemory (CpuMpData);\r
1338 FillExchangeInfoData (CpuMpData);\r
1339 SaveLocalApicTimerSetting (CpuMpData);\r
1340 }\r
1341\r
1342 if (CpuMpData->ApLoopMode == ApInMwaitLoop) {\r
1343 //\r
1344 // Get AP target C-state each time when waking up AP,\r
1345 // for it maybe updated by platform again\r
1346 //\r
1347 CpuMpData->ApTargetCState = PcdGet8 (PcdCpuApTargetCstate);\r
1348 }\r
1349\r
1350 ExchangeInfo = CpuMpData->MpCpuExchangeInfo;\r
1351\r
1352 if (Broadcast) {\r
1353 for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
1354 if (Index != CpuMpData->BspNumber) {\r
1355 CpuData = &CpuMpData->CpuData[Index];\r
1356 //\r
1357 // All AP(include disabled AP) will be woke up by INIT-SIPI-SIPI, but\r
1358 // the AP procedure will be skipped for disabled AP because AP state\r
1359 // is not CpuStateReady.\r
1360 //\r
1361 if (GetApState (CpuData) == CpuStateDisabled && !WakeUpDisabledAps) {\r
1362 continue;\r
1363 }\r
1364\r
1365 CpuData->ApFunction = (UINTN) Procedure;\r
1366 CpuData->ApFunctionArgument = (UINTN) ProcedureArgument;\r
1367 SetApState (CpuData, CpuStateReady);\r
1368 if (CpuMpData->InitFlag != ApInitConfig) {\r
1369 *(UINT32 *) CpuData->StartupApSignal = WAKEUP_AP_SIGNAL;\r
1370 }\r
1371 }\r
1372 }\r
1373 if (ResetVectorRequired) {\r
1374 //\r
1375 // For SEV-ES, the initial AP boot address will be defined by\r
1376 // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address\r
1377 // from the original INIT-SIPI-SIPI.\r
1378 //\r
1379 if (CpuMpData->SevEsIsEnabled) {\r
1380 SetSevEsJumpTable (ExchangeInfo->BufferStart);\r
1381 }\r
1382\r
1383 //\r
1384 // Wakeup all APs\r
1385 //\r
1386 SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart);\r
1387 }\r
1388 if (CpuMpData->InitFlag == ApInitConfig) {\r
1389 if (PcdGet32 (PcdCpuBootLogicalProcessorNumber) > 0) {\r
1390 //\r
1391 // The AP enumeration algorithm below is suitable only when the\r
1392 // platform can tell us the *exact* boot CPU count in advance.\r
1393 //\r
1394 // The wait below finishes only when the detected AP count reaches\r
1395 // (PcdCpuBootLogicalProcessorNumber - 1), regardless of how long that\r
1396 // takes. If at least one AP fails to check in (meaning a platform\r
1397 // hardware bug), the detection hangs forever, by design. If the actual\r
1398 // boot CPU count in the system is higher than\r
1399 // PcdCpuBootLogicalProcessorNumber (meaning a platform\r
1400 // misconfiguration), then some APs may complete initialization after\r
1401 // the wait finishes, and cause undefined behavior.\r
1402 //\r
1403 TimedWaitForApFinish (\r
1404 CpuMpData,\r
1405 PcdGet32 (PcdCpuBootLogicalProcessorNumber) - 1,\r
1406 MAX_UINT32 // approx. 71 minutes\r
1407 );\r
1408 } else {\r
1409 //\r
1410 // The AP enumeration algorithm below is suitable for two use cases.\r
1411 //\r
1412 // (1) The check-in time for an individual AP is bounded, and APs run\r
1413 // through their initialization routines strongly concurrently. In\r
1414 // particular, the number of concurrently running APs\r
1415 // ("NumApsExecuting") is never expected to fall to zero\r
1416 // *temporarily* -- it is expected to fall to zero only when all\r
1417 // APs have checked-in.\r
1418 //\r
1419 // In this case, the platform is supposed to set\r
1420 // PcdCpuApInitTimeOutInMicroSeconds to a low-ish value (just long\r
1421 // enough for one AP to start initialization). The timeout will be\r
1422 // reached soon, and remaining APs are collected by watching\r
1423 // NumApsExecuting fall to zero. If NumApsExecuting falls to zero\r
1424 // mid-process, while some APs have not completed initialization,\r
1425 // the behavior is undefined.\r
1426 //\r
1427 // (2) The check-in time for an individual AP is unbounded, and/or APs\r
1428 // may complete their initializations widely spread out. In\r
1429 // particular, some APs may finish initialization before some APs\r
1430 // even start.\r
1431 //\r
1432 // In this case, the platform is supposed to set\r
1433 // PcdCpuApInitTimeOutInMicroSeconds to a high-ish value. The AP\r
1434 // enumeration will always take that long (except when the boot CPU\r
1435 // count happens to be maximal, that is,\r
1436 // PcdCpuMaxLogicalProcessorNumber). All APs are expected to\r
1437 // check-in before the timeout, and NumApsExecuting is assumed zero\r
1438 // at timeout. APs that miss the time-out may cause undefined\r
1439 // behavior.\r
1440 //\r
1441 TimedWaitForApFinish (\r
1442 CpuMpData,\r
1443 PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1,\r
1444 PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds)\r
1445 );\r
1446\r
1447 while (CpuMpData->MpCpuExchangeInfo->NumApsExecuting != 0) {\r
1448 CpuPause();\r
1449 }\r
1450 }\r
1451 } else {\r
1452 //\r
1453 // Wait all APs waken up if this is not the 1st broadcast of SIPI\r
1454 //\r
1455 for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
1456 CpuData = &CpuMpData->CpuData[Index];\r
1457 if (Index != CpuMpData->BspNumber) {\r
1458 WaitApWakeup (CpuData->StartupApSignal);\r
1459 }\r
1460 }\r
1461 }\r
1462 } else {\r
1463 CpuData = &CpuMpData->CpuData[ProcessorNumber];\r
1464 CpuData->ApFunction = (UINTN) Procedure;\r
1465 CpuData->ApFunctionArgument = (UINTN) ProcedureArgument;\r
1466 SetApState (CpuData, CpuStateReady);\r
1467 //\r
1468 // Wakeup specified AP\r
1469 //\r
1470 ASSERT (CpuMpData->InitFlag != ApInitConfig);\r
1471 *(UINT32 *) CpuData->StartupApSignal = WAKEUP_AP_SIGNAL;\r
1472 if (ResetVectorRequired) {\r
1473 CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
1474\r
1475 //\r
1476 // For SEV-ES, the initial AP boot address will be defined by\r
1477 // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address\r
1478 // from the original INIT-SIPI-SIPI.\r
1479 //\r
1480 if (CpuMpData->SevEsIsEnabled) {\r
1481 SetSevEsJumpTable (ExchangeInfo->BufferStart);\r
1482 }\r
1483\r
1484 SendInitSipiSipi (\r
1485 CpuInfoInHob[ProcessorNumber].ApicId,\r
1486 (UINT32) ExchangeInfo->BufferStart\r
1487 );\r
1488 }\r
1489 //\r
1490 // Wait specified AP waken up\r
1491 //\r
1492 WaitApWakeup (CpuData->StartupApSignal);\r
1493 }\r
1494\r
1495 if (ResetVectorRequired) {\r
1496 FreeResetVector (CpuMpData);\r
1497 }\r
1498\r
1499 //\r
1500 // After one round of Wakeup Ap actions, need to re-sync ApLoopMode with\r
1501 // WakeUpByInitSipiSipi flag. WakeUpByInitSipiSipi flag maybe changed by\r
1502 // S3SmmInitDone Ppi.\r
1503 //\r
1504 CpuMpData->WakeUpByInitSipiSipi = (CpuMpData->ApLoopMode == ApInHltLoop);\r
1505}\r
1506\r
1507/**\r
1508 Calculate timeout value and return the current performance counter value.\r
1509\r
1510 Calculate the number of performance counter ticks required for a timeout.\r
1511 If TimeoutInMicroseconds is 0, return value is also 0, which is recognized\r
1512 as infinity.\r
1513\r
1514 @param[in] TimeoutInMicroseconds Timeout value in microseconds.\r
1515 @param[out] CurrentTime Returns the current value of the performance counter.\r
1516\r
1517 @return Expected time stamp counter for timeout.\r
1518 If TimeoutInMicroseconds is 0, return value is also 0, which is recognized\r
1519 as infinity.\r
1520\r
1521**/\r
1522UINT64\r
1523CalculateTimeout (\r
1524 IN UINTN TimeoutInMicroseconds,\r
1525 OUT UINT64 *CurrentTime\r
1526 )\r
1527{\r
1528 UINT64 TimeoutInSeconds;\r
1529 UINT64 TimestampCounterFreq;\r
1530\r
1531 //\r
1532 // Read the current value of the performance counter\r
1533 //\r
1534 *CurrentTime = GetPerformanceCounter ();\r
1535\r
1536 //\r
1537 // If TimeoutInMicroseconds is 0, return value is also 0, which is recognized\r
1538 // as infinity.\r
1539 //\r
1540 if (TimeoutInMicroseconds == 0) {\r
1541 return 0;\r
1542 }\r
1543\r
1544 //\r
1545 // GetPerformanceCounterProperties () returns the timestamp counter's frequency\r
1546 // in Hz.\r
1547 //\r
1548 TimestampCounterFreq = GetPerformanceCounterProperties (NULL, NULL);\r
1549\r
1550 //\r
1551 // Check the potential overflow before calculate the number of ticks for the timeout value.\r
1552 //\r
1553 if (DivU64x64Remainder (MAX_UINT64, TimeoutInMicroseconds, NULL) < TimestampCounterFreq) {\r
1554 //\r
1555 // Convert microseconds into seconds if direct multiplication overflows\r
1556 //\r
1557 TimeoutInSeconds = DivU64x32 (TimeoutInMicroseconds, 1000000);\r
1558 //\r
1559 // Assertion if the final tick count exceeds MAX_UINT64\r
1560 //\r
1561 ASSERT (DivU64x64Remainder (MAX_UINT64, TimeoutInSeconds, NULL) >= TimestampCounterFreq);\r
1562 return MultU64x64 (TimestampCounterFreq, TimeoutInSeconds);\r
1563 } else {\r
1564 //\r
1565 // No overflow case, multiply the return value with TimeoutInMicroseconds and then divide\r
1566 // it by 1,000,000, to get the number of ticks for the timeout value.\r
1567 //\r
1568 return DivU64x32 (\r
1569 MultU64x64 (\r
1570 TimestampCounterFreq,\r
1571 TimeoutInMicroseconds\r
1572 ),\r
1573 1000000\r
1574 );\r
1575 }\r
1576}\r
1577\r
1578/**\r
1579 Checks whether timeout expires.\r
1580\r
1581 Check whether the number of elapsed performance counter ticks required for\r
1582 a timeout condition has been reached.\r
1583 If Timeout is zero, which means infinity, return value is always FALSE.\r
1584\r
1585 @param[in, out] PreviousTime On input, the value of the performance counter\r
1586 when it was last read.\r
1587 On output, the current value of the performance\r
1588 counter\r
1589 @param[in] TotalTime The total amount of elapsed time in performance\r
1590 counter ticks.\r
1591 @param[in] Timeout The number of performance counter ticks required\r
1592 to reach a timeout condition.\r
1593\r
1594 @retval TRUE A timeout condition has been reached.\r
1595 @retval FALSE A timeout condition has not been reached.\r
1596\r
1597**/\r
1598BOOLEAN\r
1599CheckTimeout (\r
1600 IN OUT UINT64 *PreviousTime,\r
1601 IN UINT64 *TotalTime,\r
1602 IN UINT64 Timeout\r
1603 )\r
1604{\r
1605 UINT64 Start;\r
1606 UINT64 End;\r
1607 UINT64 CurrentTime;\r
1608 INT64 Delta;\r
1609 INT64 Cycle;\r
1610\r
1611 if (Timeout == 0) {\r
1612 return FALSE;\r
1613 }\r
1614 GetPerformanceCounterProperties (&Start, &End);\r
1615 Cycle = End - Start;\r
1616 if (Cycle < 0) {\r
1617 Cycle = -Cycle;\r
1618 }\r
1619 Cycle++;\r
1620 CurrentTime = GetPerformanceCounter();\r
1621 Delta = (INT64) (CurrentTime - *PreviousTime);\r
1622 if (Start > End) {\r
1623 Delta = -Delta;\r
1624 }\r
1625 if (Delta < 0) {\r
1626 Delta += Cycle;\r
1627 }\r
1628 *TotalTime += Delta;\r
1629 *PreviousTime = CurrentTime;\r
1630 if (*TotalTime > Timeout) {\r
1631 return TRUE;\r
1632 }\r
1633 return FALSE;\r
1634}\r
1635\r
1636/**\r
1637 Helper function that waits until the finished AP count reaches the specified\r
1638 limit, or the specified timeout elapses (whichever comes first).\r
1639\r
1640 @param[in] CpuMpData Pointer to CPU MP Data.\r
1641 @param[in] FinishedApLimit The number of finished APs to wait for.\r
1642 @param[in] TimeLimit The number of microseconds to wait for.\r
1643**/\r
1644VOID\r
1645TimedWaitForApFinish (\r
1646 IN CPU_MP_DATA *CpuMpData,\r
1647 IN UINT32 FinishedApLimit,\r
1648 IN UINT32 TimeLimit\r
1649 )\r
1650{\r
1651 //\r
1652 // CalculateTimeout() and CheckTimeout() consider a TimeLimit of 0\r
1653 // "infinity", so check for (TimeLimit == 0) explicitly.\r
1654 //\r
1655 if (TimeLimit == 0) {\r
1656 return;\r
1657 }\r
1658\r
1659 CpuMpData->TotalTime = 0;\r
1660 CpuMpData->ExpectedTime = CalculateTimeout (\r
1661 TimeLimit,\r
1662 &CpuMpData->CurrentTime\r
1663 );\r
1664 while (CpuMpData->FinishedCount < FinishedApLimit &&\r
1665 !CheckTimeout (\r
1666 &CpuMpData->CurrentTime,\r
1667 &CpuMpData->TotalTime,\r
1668 CpuMpData->ExpectedTime\r
1669 )) {\r
1670 CpuPause ();\r
1671 }\r
1672\r
1673 if (CpuMpData->FinishedCount >= FinishedApLimit) {\r
1674 DEBUG ((\r
1675 DEBUG_VERBOSE,\r
1676 "%a: reached FinishedApLimit=%u in %Lu microseconds\n",\r
1677 __FUNCTION__,\r
1678 FinishedApLimit,\r
1679 DivU64x64Remainder (\r
1680 MultU64x32 (CpuMpData->TotalTime, 1000000),\r
1681 GetPerformanceCounterProperties (NULL, NULL),\r
1682 NULL\r
1683 )\r
1684 ));\r
1685 }\r
1686}\r
1687\r
1688/**\r
1689 Reset an AP to Idle state.\r
1690\r
1691 Any task being executed by the AP will be aborted and the AP\r
1692 will be waiting for a new task in Wait-For-SIPI state.\r
1693\r
1694 @param[in] ProcessorNumber The handle number of processor.\r
1695**/\r
1696VOID\r
1697ResetProcessorToIdleState (\r
1698 IN UINTN ProcessorNumber\r
1699 )\r
1700{\r
1701 CPU_MP_DATA *CpuMpData;\r
1702\r
1703 CpuMpData = GetCpuMpData ();\r
1704\r
1705 CpuMpData->InitFlag = ApInitReconfig;\r
1706 WakeUpAP (CpuMpData, FALSE, ProcessorNumber, NULL, NULL, TRUE);\r
1707 while (CpuMpData->FinishedCount < 1) {\r
1708 CpuPause ();\r
1709 }\r
1710 CpuMpData->InitFlag = ApInitDone;\r
1711\r
1712 SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);\r
1713}\r
1714\r
1715/**\r
1716 Searches for the next waiting AP.\r
1717\r
1718 Search for the next AP that is put in waiting state by single-threaded StartupAllAPs().\r
1719\r
1720 @param[out] NextProcessorNumber Pointer to the processor number of the next waiting AP.\r
1721\r
1722 @retval EFI_SUCCESS The next waiting AP has been found.\r
1723 @retval EFI_NOT_FOUND No waiting AP exists.\r
1724\r
1725**/\r
1726EFI_STATUS\r
1727GetNextWaitingProcessorNumber (\r
1728 OUT UINTN *NextProcessorNumber\r
1729 )\r
1730{\r
1731 UINTN ProcessorNumber;\r
1732 CPU_MP_DATA *CpuMpData;\r
1733\r
1734 CpuMpData = GetCpuMpData ();\r
1735\r
1736 for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) {\r
1737 if (CpuMpData->CpuData[ProcessorNumber].Waiting) {\r
1738 *NextProcessorNumber = ProcessorNumber;\r
1739 return EFI_SUCCESS;\r
1740 }\r
1741 }\r
1742\r
1743 return EFI_NOT_FOUND;\r
1744}\r
1745\r
1746/** Checks status of specified AP.\r
1747\r
1748 This function checks whether the specified AP has finished the task assigned\r
1749 by StartupThisAP(), and whether timeout expires.\r
1750\r
1751 @param[in] ProcessorNumber The handle number of processor.\r
1752\r
1753 @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs().\r
1754 @retval EFI_TIMEOUT The timeout expires.\r
1755 @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired.\r
1756**/\r
1757EFI_STATUS\r
1758CheckThisAP (\r
1759 IN UINTN ProcessorNumber\r
1760 )\r
1761{\r
1762 CPU_MP_DATA *CpuMpData;\r
1763 CPU_AP_DATA *CpuData;\r
1764\r
1765 CpuMpData = GetCpuMpData ();\r
1766 CpuData = &CpuMpData->CpuData[ProcessorNumber];\r
1767\r
1768 //\r
1769 // Check the CPU state of AP. If it is CpuStateIdle, then the AP has finished its task.\r
1770 // Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the\r
1771 // value of state after setting the it to CpuStateIdle, so BSP can safely make use of its value.\r
1772 //\r
1773 //\r
1774 // If the AP finishes for StartupThisAP(), return EFI_SUCCESS.\r
1775 //\r
1776 if (GetApState(CpuData) == CpuStateFinished) {\r
1777 if (CpuData->Finished != NULL) {\r
1778 *(CpuData->Finished) = TRUE;\r
1779 }\r
1780 SetApState (CpuData, CpuStateIdle);\r
1781 return EFI_SUCCESS;\r
1782 } else {\r
1783 //\r
1784 // If timeout expires for StartupThisAP(), report timeout.\r
1785 //\r
1786 if (CheckTimeout (&CpuData->CurrentTime, &CpuData->TotalTime, CpuData->ExpectedTime)) {\r
1787 if (CpuData->Finished != NULL) {\r
1788 *(CpuData->Finished) = FALSE;\r
1789 }\r
1790 //\r
1791 // Reset failed AP to idle state\r
1792 //\r
1793 ResetProcessorToIdleState (ProcessorNumber);\r
1794\r
1795 return EFI_TIMEOUT;\r
1796 }\r
1797 }\r
1798 return EFI_NOT_READY;\r
1799}\r
1800\r
1801/**\r
1802 Checks status of all APs.\r
1803\r
1804 This function checks whether all APs have finished task assigned by StartupAllAPs(),\r
1805 and whether timeout expires.\r
1806\r
1807 @retval EFI_SUCCESS All APs have finished task assigned by StartupAllAPs().\r
1808 @retval EFI_TIMEOUT The timeout expires.\r
1809 @retval EFI_NOT_READY APs have not finished task and timeout has not expired.\r
1810**/\r
1811EFI_STATUS\r
1812CheckAllAPs (\r
1813 VOID\r
1814 )\r
1815{\r
1816 UINTN ProcessorNumber;\r
1817 UINTN NextProcessorNumber;\r
1818 UINTN ListIndex;\r
1819 EFI_STATUS Status;\r
1820 CPU_MP_DATA *CpuMpData;\r
1821 CPU_AP_DATA *CpuData;\r
1822\r
1823 CpuMpData = GetCpuMpData ();\r
1824\r
1825 NextProcessorNumber = 0;\r
1826\r
1827 //\r
1828 // Go through all APs that are responsible for the StartupAllAPs().\r
1829 //\r
1830 for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) {\r
1831 if (!CpuMpData->CpuData[ProcessorNumber].Waiting) {\r
1832 continue;\r
1833 }\r
1834\r
1835 CpuData = &CpuMpData->CpuData[ProcessorNumber];\r
1836 //\r
1837 // Check the CPU state of AP. If it is CpuStateIdle, then the AP has finished its task.\r
1838 // Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the\r
1839 // value of state after setting the it to CpuStateIdle, so BSP can safely make use of its value.\r
1840 //\r
1841 if (GetApState(CpuData) == CpuStateFinished) {\r
1842 CpuMpData->RunningCount --;\r
1843 CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE;\r
1844 SetApState(CpuData, CpuStateIdle);\r
1845\r
1846 //\r
1847 // If in Single Thread mode, then search for the next waiting AP for execution.\r
1848 //\r
1849 if (CpuMpData->SingleThread) {\r
1850 Status = GetNextWaitingProcessorNumber (&NextProcessorNumber);\r
1851\r
1852 if (!EFI_ERROR (Status)) {\r
1853 WakeUpAP (\r
1854 CpuMpData,\r
1855 FALSE,\r
1856 (UINT32) NextProcessorNumber,\r
1857 CpuMpData->Procedure,\r
1858 CpuMpData->ProcArguments,\r
1859 TRUE\r
1860 );\r
1861 }\r
1862 }\r
1863 }\r
1864 }\r
1865\r
1866 //\r
1867 // If all APs finish, return EFI_SUCCESS.\r
1868 //\r
1869 if (CpuMpData->RunningCount == 0) {\r
1870 return EFI_SUCCESS;\r
1871 }\r
1872\r
1873 //\r
1874 // If timeout expires, report timeout.\r
1875 //\r
1876 if (CheckTimeout (\r
1877 &CpuMpData->CurrentTime,\r
1878 &CpuMpData->TotalTime,\r
1879 CpuMpData->ExpectedTime)\r
1880 ) {\r
1881 //\r
1882 // If FailedCpuList is not NULL, record all failed APs in it.\r
1883 //\r
1884 if (CpuMpData->FailedCpuList != NULL) {\r
1885 *CpuMpData->FailedCpuList =\r
1886 AllocatePool ((CpuMpData->RunningCount + 1) * sizeof (UINTN));\r
1887 ASSERT (*CpuMpData->FailedCpuList != NULL);\r
1888 }\r
1889 ListIndex = 0;\r
1890\r
1891 for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) {\r
1892 //\r
1893 // Check whether this processor is responsible for StartupAllAPs().\r
1894 //\r
1895 if (CpuMpData->CpuData[ProcessorNumber].Waiting) {\r
1896 //\r
1897 // Reset failed APs to idle state\r
1898 //\r
1899 ResetProcessorToIdleState (ProcessorNumber);\r
1900 CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE;\r
1901 if (CpuMpData->FailedCpuList != NULL) {\r
1902 (*CpuMpData->FailedCpuList)[ListIndex++] = ProcessorNumber;\r
1903 }\r
1904 }\r
1905 }\r
1906 if (CpuMpData->FailedCpuList != NULL) {\r
1907 (*CpuMpData->FailedCpuList)[ListIndex] = END_OF_CPU_LIST;\r
1908 }\r
1909 return EFI_TIMEOUT;\r
1910 }\r
1911 return EFI_NOT_READY;\r
1912}\r
1913\r
1914/**\r
1915 MP Initialize Library initialization.\r
1916\r
1917 This service will allocate AP reset vector and wakeup all APs to do APs\r
1918 initialization.\r
1919\r
1920 This service must be invoked before all other MP Initialize Library\r
1921 service are invoked.\r
1922\r
1923 @retval EFI_SUCCESS MP initialization succeeds.\r
1924 @retval Others MP initialization fails.\r
1925\r
1926**/\r
1927EFI_STATUS\r
1928EFIAPI\r
1929MpInitLibInitialize (\r
1930 VOID\r
1931 )\r
1932{\r
1933 CPU_MP_DATA *OldCpuMpData;\r
1934 CPU_INFO_IN_HOB *CpuInfoInHob;\r
1935 UINT32 MaxLogicalProcessorNumber;\r
1936 UINT32 ApStackSize;\r
1937 MP_ASSEMBLY_ADDRESS_MAP AddressMap;\r
1938 CPU_VOLATILE_REGISTERS VolatileRegisters;\r
1939 UINTN BufferSize;\r
1940 UINT32 MonitorFilterSize;\r
1941 VOID *MpBuffer;\r
1942 UINTN Buffer;\r
1943 CPU_MP_DATA *CpuMpData;\r
1944 UINT8 ApLoopMode;\r
1945 UINT8 *MonitorBuffer;\r
1946 UINTN Index;\r
1947 UINTN ApResetVectorSize;\r
1948 UINTN BackupBufferAddr;\r
1949 UINTN ApIdtBase;\r
1950\r
1951 OldCpuMpData = GetCpuMpDataFromGuidedHob ();\r
1952 if (OldCpuMpData == NULL) {\r
1953 MaxLogicalProcessorNumber = PcdGet32(PcdCpuMaxLogicalProcessorNumber);\r
1954 } else {\r
1955 MaxLogicalProcessorNumber = OldCpuMpData->CpuCount;\r
1956 }\r
1957 ASSERT (MaxLogicalProcessorNumber != 0);\r
1958\r
1959 AsmGetAddressMap (&AddressMap);\r
1960 ApResetVectorSize = GetApResetVectorSize (&AddressMap);\r
1961 ApStackSize = PcdGet32(PcdCpuApStackSize);\r
1962 ApLoopMode = GetApLoopMode (&MonitorFilterSize);\r
1963\r
1964 //\r
1965 // Save BSP's Control registers for APs.\r
1966 //\r
1967 SaveVolatileRegisters (&VolatileRegisters);\r
1968\r
1969 BufferSize = ApStackSize * MaxLogicalProcessorNumber;\r
1970 BufferSize += MonitorFilterSize * MaxLogicalProcessorNumber;\r
1971 BufferSize += ApResetVectorSize;\r
1972 BufferSize = ALIGN_VALUE (BufferSize, 8);\r
1973 BufferSize += VolatileRegisters.Idtr.Limit + 1;\r
1974 BufferSize += sizeof (CPU_MP_DATA);\r
1975 BufferSize += (sizeof (CPU_AP_DATA) + sizeof (CPU_INFO_IN_HOB))* MaxLogicalProcessorNumber;\r
1976 MpBuffer = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize));\r
1977 ASSERT (MpBuffer != NULL);\r
1978 ZeroMem (MpBuffer, BufferSize);\r
1979 Buffer = (UINTN) MpBuffer;\r
1980\r
1981 //\r
1982 // The layout of the Buffer is as below:\r
1983 //\r
1984 // +--------------------+ <-- Buffer\r
1985 // AP Stacks (N)\r
1986 // +--------------------+ <-- MonitorBuffer\r
1987 // AP Monitor Filters (N)\r
1988 // +--------------------+ <-- BackupBufferAddr (CpuMpData->BackupBuffer)\r
1989 // Backup Buffer\r
1990 // +--------------------+\r
1991 // Padding\r
1992 // +--------------------+ <-- ApIdtBase (8-byte boundary)\r
1993 // AP IDT All APs share one separate IDT. So AP can get address of CPU_MP_DATA from IDT Base.\r
1994 // +--------------------+ <-- CpuMpData\r
1995 // CPU_MP_DATA\r
1996 // +--------------------+ <-- CpuMpData->CpuData\r
1997 // CPU_AP_DATA (N)\r
1998 // +--------------------+ <-- CpuMpData->CpuInfoInHob\r
1999 // CPU_INFO_IN_HOB (N)\r
2000 // +--------------------+\r
2001 //\r
2002 MonitorBuffer = (UINT8 *) (Buffer + ApStackSize * MaxLogicalProcessorNumber);\r
2003 BackupBufferAddr = (UINTN) MonitorBuffer + MonitorFilterSize * MaxLogicalProcessorNumber;\r
2004 ApIdtBase = ALIGN_VALUE (BackupBufferAddr + ApResetVectorSize, 8);\r
2005 CpuMpData = (CPU_MP_DATA *) (ApIdtBase + VolatileRegisters.Idtr.Limit + 1);\r
2006 CpuMpData->Buffer = Buffer;\r
2007 CpuMpData->CpuApStackSize = ApStackSize;\r
2008 CpuMpData->BackupBuffer = BackupBufferAddr;\r
2009 CpuMpData->BackupBufferSize = ApResetVectorSize;\r
2010 CpuMpData->WakeupBuffer = (UINTN) -1;\r
2011 CpuMpData->CpuCount = 1;\r
2012 CpuMpData->BspNumber = 0;\r
2013 CpuMpData->WaitEvent = NULL;\r
2014 CpuMpData->SwitchBspFlag = FALSE;\r
2015 CpuMpData->CpuData = (CPU_AP_DATA *) (CpuMpData + 1);\r
2016 CpuMpData->CpuInfoInHob = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber);\r
2017 InitializeSpinLock(&CpuMpData->MpLock);\r
2018 CpuMpData->SevEsIsEnabled = PcdGetBool (PcdSevEsIsEnabled);\r
2019 CpuMpData->SevEsAPBuffer = (UINTN) -1;\r
2020 CpuMpData->GhcbBase = PcdGet64 (PcdGhcbBase);\r
2021\r
2022 //\r
2023 // Make sure no memory usage outside of the allocated buffer.\r
2024 //\r
2025 ASSERT ((CpuMpData->CpuInfoInHob + sizeof (CPU_INFO_IN_HOB) * MaxLogicalProcessorNumber) ==\r
2026 Buffer + BufferSize);\r
2027\r
2028 //\r
2029 // Duplicate BSP's IDT to APs.\r
2030 // All APs share one separate IDT. So AP can get the address of CpuMpData by using IDTR.BASE + IDTR.LIMIT + 1\r
2031 //\r
2032 CopyMem ((VOID *)ApIdtBase, (VOID *)VolatileRegisters.Idtr.Base, VolatileRegisters.Idtr.Limit + 1);\r
2033 VolatileRegisters.Idtr.Base = ApIdtBase;\r
2034 //\r
2035 // Don't pass BSP's TR to APs to avoid AP init failure.\r
2036 //\r
2037 VolatileRegisters.Tr = 0;\r
2038 CopyMem (&CpuMpData->CpuData[0].VolatileRegisters, &VolatileRegisters, sizeof (VolatileRegisters));\r
2039 //\r
2040 // Set BSP basic information\r
2041 //\r
2042 InitializeApData (CpuMpData, 0, 0, CpuMpData->Buffer + ApStackSize);\r
2043 //\r
2044 // Save assembly code information\r
2045 //\r
2046 CopyMem (&CpuMpData->AddressMap, &AddressMap, sizeof (MP_ASSEMBLY_ADDRESS_MAP));\r
2047 //\r
2048 // Finally set AP loop mode\r
2049 //\r
2050 CpuMpData->ApLoopMode = ApLoopMode;\r
2051 DEBUG ((DEBUG_INFO, "AP Loop Mode is %d\n", CpuMpData->ApLoopMode));\r
2052\r
2053 CpuMpData->WakeUpByInitSipiSipi = (CpuMpData->ApLoopMode == ApInHltLoop);\r
2054\r
2055 //\r
2056 // Set up APs wakeup signal buffer\r
2057 //\r
2058 for (Index = 0; Index < MaxLogicalProcessorNumber; Index++) {\r
2059 CpuMpData->CpuData[Index].StartupApSignal =\r
2060 (UINT32 *)(MonitorBuffer + MonitorFilterSize * Index);\r
2061 }\r
2062 //\r
2063 // Enable the local APIC for Virtual Wire Mode.\r
2064 //\r
2065 ProgramVirtualWireMode ();\r
2066\r
2067 if (OldCpuMpData == NULL) {\r
2068 if (MaxLogicalProcessorNumber > 1) {\r
2069 //\r
2070 // Wakeup all APs and calculate the processor count in system\r
2071 //\r
2072 CollectProcessorCount (CpuMpData);\r
2073 }\r
2074 } else {\r
2075 //\r
2076 // APs have been wakeup before, just get the CPU Information\r
2077 // from HOB\r
2078 //\r
2079 OldCpuMpData->NewCpuMpData = CpuMpData;\r
2080 CpuMpData->CpuCount = OldCpuMpData->CpuCount;\r
2081 CpuMpData->BspNumber = OldCpuMpData->BspNumber;\r
2082 CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob;\r
2083 CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
2084 for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
2085 InitializeSpinLock(&CpuMpData->CpuData[Index].ApLock);\r
2086 CpuMpData->CpuData[Index].CpuHealthy = (CpuInfoInHob[Index].Health == 0)? TRUE:FALSE;\r
2087 CpuMpData->CpuData[Index].ApFunction = 0;\r
2088 }\r
2089 }\r
2090\r
2091 if (!GetMicrocodePatchInfoFromHob (\r
2092 &CpuMpData->MicrocodePatchAddress,\r
2093 &CpuMpData->MicrocodePatchRegionSize\r
2094 )) {\r
2095 //\r
2096 // The microcode patch information cache HOB does not exist, which means\r
2097 // the microcode patches data has not been loaded into memory yet\r
2098 //\r
2099 ShadowMicrocodeUpdatePatch (CpuMpData);\r
2100 }\r
2101\r
2102 //\r
2103 // Detect and apply Microcode on BSP\r
2104 //\r
2105 MicrocodeDetect (CpuMpData, CpuMpData->BspNumber);\r
2106 //\r
2107 // Store BSP's MTRR setting\r
2108 //\r
2109 MtrrGetAllMtrrs (&CpuMpData->MtrrTable);\r
2110\r
2111 //\r
2112 // Wakeup APs to do some AP initialize sync (Microcode & MTRR)\r
2113 //\r
2114 if (CpuMpData->CpuCount > 1) {\r
2115 if (OldCpuMpData != NULL) {\r
2116 //\r
2117 // Only needs to use this flag for DXE phase to update the wake up\r
2118 // buffer. Wakeup buffer allocated in PEI phase is no longer valid\r
2119 // in DXE.\r
2120 //\r
2121 CpuMpData->InitFlag = ApInitReconfig;\r
2122 }\r
2123 WakeUpAP (CpuMpData, TRUE, 0, ApInitializeSync, CpuMpData, TRUE);\r
2124 //\r
2125 // Wait for all APs finished initialization\r
2126 //\r
2127 while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) {\r
2128 CpuPause ();\r
2129 }\r
2130 if (OldCpuMpData != NULL) {\r
2131 CpuMpData->InitFlag = ApInitDone;\r
2132 }\r
2133 for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
2134 SetApState (&CpuMpData->CpuData[Index], CpuStateIdle);\r
2135 }\r
2136 }\r
2137\r
2138 //\r
2139 // Initialize global data for MP support\r
2140 //\r
2141 InitMpGlobalData (CpuMpData);\r
2142\r
2143 return EFI_SUCCESS;\r
2144}\r
2145\r
2146/**\r
2147 Gets detailed MP-related information on the requested processor at the\r
2148 instant this call is made. This service may only be called from the BSP.\r
2149\r
2150 @param[in] ProcessorNumber The handle number of processor.\r
2151 @param[out] ProcessorInfoBuffer A pointer to the buffer where information for\r
2152 the requested processor is deposited.\r
2153 @param[out] HealthData Return processor health data.\r
2154\r
2155 @retval EFI_SUCCESS Processor information was returned.\r
2156 @retval EFI_DEVICE_ERROR The calling processor is an AP.\r
2157 @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL.\r
2158 @retval EFI_NOT_FOUND The processor with the handle specified by\r
2159 ProcessorNumber does not exist in the platform.\r
2160 @retval EFI_NOT_READY MP Initialize Library is not initialized.\r
2161\r
2162**/\r
2163EFI_STATUS\r
2164EFIAPI\r
2165MpInitLibGetProcessorInfo (\r
2166 IN UINTN ProcessorNumber,\r
2167 OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer,\r
2168 OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL\r
2169 )\r
2170{\r
2171 CPU_MP_DATA *CpuMpData;\r
2172 UINTN CallerNumber;\r
2173 CPU_INFO_IN_HOB *CpuInfoInHob;\r
2174 UINTN OriginalProcessorNumber;\r
2175\r
2176 CpuMpData = GetCpuMpData ();\r
2177 CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
2178\r
2179 //\r
2180 // Lower 24 bits contains the actual processor number.\r
2181 //\r
2182 OriginalProcessorNumber = ProcessorNumber;\r
2183 ProcessorNumber &= BIT24 - 1;\r
2184\r
2185 //\r
2186 // Check whether caller processor is BSP\r
2187 //\r
2188 MpInitLibWhoAmI (&CallerNumber);\r
2189 if (CallerNumber != CpuMpData->BspNumber) {\r
2190 return EFI_DEVICE_ERROR;\r
2191 }\r
2192\r
2193 if (ProcessorInfoBuffer == NULL) {\r
2194 return EFI_INVALID_PARAMETER;\r
2195 }\r
2196\r
2197 if (ProcessorNumber >= CpuMpData->CpuCount) {\r
2198 return EFI_NOT_FOUND;\r
2199 }\r
2200\r
2201 ProcessorInfoBuffer->ProcessorId = (UINT64) CpuInfoInHob[ProcessorNumber].ApicId;\r
2202 ProcessorInfoBuffer->StatusFlag = 0;\r
2203 if (ProcessorNumber == CpuMpData->BspNumber) {\r
2204 ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT;\r
2205 }\r
2206 if (CpuMpData->CpuData[ProcessorNumber].CpuHealthy) {\r
2207 ProcessorInfoBuffer->StatusFlag |= PROCESSOR_HEALTH_STATUS_BIT;\r
2208 }\r
2209 if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateDisabled) {\r
2210 ProcessorInfoBuffer->StatusFlag &= ~PROCESSOR_ENABLED_BIT;\r
2211 } else {\r
2212 ProcessorInfoBuffer->StatusFlag |= PROCESSOR_ENABLED_BIT;\r
2213 }\r
2214\r
2215 //\r
2216 // Get processor location information\r
2217 //\r
2218 GetProcessorLocationByApicId (\r
2219 CpuInfoInHob[ProcessorNumber].ApicId,\r
2220 &ProcessorInfoBuffer->Location.Package,\r
2221 &ProcessorInfoBuffer->Location.Core,\r
2222 &ProcessorInfoBuffer->Location.Thread\r
2223 );\r
2224\r
2225 if ((OriginalProcessorNumber & CPU_V2_EXTENDED_TOPOLOGY) != 0) {\r
2226 GetProcessorLocation2ByApicId (\r
2227 CpuInfoInHob[ProcessorNumber].ApicId,\r
2228 &ProcessorInfoBuffer->ExtendedInformation.Location2.Package,\r
2229 &ProcessorInfoBuffer->ExtendedInformation.Location2.Die,\r
2230 &ProcessorInfoBuffer->ExtendedInformation.Location2.Tile,\r
2231 &ProcessorInfoBuffer->ExtendedInformation.Location2.Module,\r
2232 &ProcessorInfoBuffer->ExtendedInformation.Location2.Core,\r
2233 &ProcessorInfoBuffer->ExtendedInformation.Location2.Thread\r
2234 );\r
2235 }\r
2236\r
2237 if (HealthData != NULL) {\r
2238 HealthData->Uint32 = CpuInfoInHob[ProcessorNumber].Health;\r
2239 }\r
2240\r
2241 return EFI_SUCCESS;\r
2242}\r
2243\r
2244/**\r
2245 Worker function to switch the requested AP to be the BSP from that point onward.\r
2246\r
2247 @param[in] ProcessorNumber The handle number of AP that is to become the new BSP.\r
2248 @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an\r
2249 enabled AP. Otherwise, it will be disabled.\r
2250\r
2251 @retval EFI_SUCCESS BSP successfully switched.\r
2252 @retval others Failed to switch BSP.\r
2253\r
2254**/\r
2255EFI_STATUS\r
2256SwitchBSPWorker (\r
2257 IN UINTN ProcessorNumber,\r
2258 IN BOOLEAN EnableOldBSP\r
2259 )\r
2260{\r
2261 CPU_MP_DATA *CpuMpData;\r
2262 UINTN CallerNumber;\r
2263 CPU_STATE State;\r
2264 MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr;\r
2265 BOOLEAN OldInterruptState;\r
2266 BOOLEAN OldTimerInterruptState;\r
2267\r
2268 //\r
2269 // Save and Disable Local APIC timer interrupt\r
2270 //\r
2271 OldTimerInterruptState = GetApicTimerInterruptState ();\r
2272 DisableApicTimerInterrupt ();\r
2273 //\r
2274 // Before send both BSP and AP to a procedure to exchange their roles,\r
2275 // interrupt must be disabled. This is because during the exchange role\r
2276 // process, 2 CPU may use 1 stack. If interrupt happens, the stack will\r
2277 // be corrupted, since interrupt return address will be pushed to stack\r
2278 // by hardware.\r
2279 //\r
2280 OldInterruptState = SaveAndDisableInterrupts ();\r
2281\r
2282 //\r
2283 // Mask LINT0 & LINT1 for the old BSP\r
2284 //\r
2285 DisableLvtInterrupts ();\r
2286\r
2287 CpuMpData = GetCpuMpData ();\r
2288\r
2289 //\r
2290 // Check whether caller processor is BSP\r
2291 //\r
2292 MpInitLibWhoAmI (&CallerNumber);\r
2293 if (CallerNumber != CpuMpData->BspNumber) {\r
2294 return EFI_DEVICE_ERROR;\r
2295 }\r
2296\r
2297 if (ProcessorNumber >= CpuMpData->CpuCount) {\r
2298 return EFI_NOT_FOUND;\r
2299 }\r
2300\r
2301 //\r
2302 // Check whether specified AP is disabled\r
2303 //\r
2304 State = GetApState (&CpuMpData->CpuData[ProcessorNumber]);\r
2305 if (State == CpuStateDisabled) {\r
2306 return EFI_INVALID_PARAMETER;\r
2307 }\r
2308\r
2309 //\r
2310 // Check whether ProcessorNumber specifies the current BSP\r
2311 //\r
2312 if (ProcessorNumber == CpuMpData->BspNumber) {\r
2313 return EFI_INVALID_PARAMETER;\r
2314 }\r
2315\r
2316 //\r
2317 // Check whether specified AP is busy\r
2318 //\r
2319 if (State == CpuStateBusy) {\r
2320 return EFI_NOT_READY;\r
2321 }\r
2322\r
2323 CpuMpData->BSPInfo.State = CPU_SWITCH_STATE_IDLE;\r
2324 CpuMpData->APInfo.State = CPU_SWITCH_STATE_IDLE;\r
2325 CpuMpData->SwitchBspFlag = TRUE;\r
2326 CpuMpData->NewBspNumber = ProcessorNumber;\r
2327\r
2328 //\r
2329 // Clear the BSP bit of MSR_IA32_APIC_BASE\r
2330 //\r
2331 ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);\r
2332 ApicBaseMsr.Bits.BSP = 0;\r
2333 AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);\r
2334\r
2335 //\r
2336 // Need to wakeUp AP (future BSP).\r
2337 //\r
2338 WakeUpAP (CpuMpData, FALSE, ProcessorNumber, FutureBSPProc, CpuMpData, TRUE);\r
2339\r
2340 AsmExchangeRole (&CpuMpData->BSPInfo, &CpuMpData->APInfo);\r
2341\r
2342 //\r
2343 // Set the BSP bit of MSR_IA32_APIC_BASE on new BSP\r
2344 //\r
2345 ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);\r
2346 ApicBaseMsr.Bits.BSP = 1;\r
2347 AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);\r
2348 ProgramVirtualWireMode ();\r
2349\r
2350 //\r
2351 // Wait for old BSP finished AP task\r
2352 //\r
2353 while (GetApState (&CpuMpData->CpuData[CallerNumber]) != CpuStateFinished) {\r
2354 CpuPause ();\r
2355 }\r
2356\r
2357 CpuMpData->SwitchBspFlag = FALSE;\r
2358 //\r
2359 // Set old BSP enable state\r
2360 //\r
2361 if (!EnableOldBSP) {\r
2362 SetApState (&CpuMpData->CpuData[CallerNumber], CpuStateDisabled);\r
2363 } else {\r
2364 SetApState (&CpuMpData->CpuData[CallerNumber], CpuStateIdle);\r
2365 }\r
2366 //\r
2367 // Save new BSP number\r
2368 //\r
2369 CpuMpData->BspNumber = (UINT32) ProcessorNumber;\r
2370\r
2371 //\r
2372 // Restore interrupt state.\r
2373 //\r
2374 SetInterruptState (OldInterruptState);\r
2375\r
2376 if (OldTimerInterruptState) {\r
2377 EnableApicTimerInterrupt ();\r
2378 }\r
2379\r
2380 return EFI_SUCCESS;\r
2381}\r
2382\r
2383/**\r
2384 Worker function to let the caller enable or disable an AP from this point onward.\r
2385 This service may only be called from the BSP.\r
2386\r
2387 @param[in] ProcessorNumber The handle number of AP.\r
2388 @param[in] EnableAP Specifies the new state for the processor for\r
2389 enabled, FALSE for disabled.\r
2390 @param[in] HealthFlag If not NULL, a pointer to a value that specifies\r
2391 the new health status of the AP.\r
2392\r
2393 @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.\r
2394 @retval others Failed to Enable/Disable AP.\r
2395\r
2396**/\r
2397EFI_STATUS\r
2398EnableDisableApWorker (\r
2399 IN UINTN ProcessorNumber,\r
2400 IN BOOLEAN EnableAP,\r
2401 IN UINT32 *HealthFlag OPTIONAL\r
2402 )\r
2403{\r
2404 CPU_MP_DATA *CpuMpData;\r
2405 UINTN CallerNumber;\r
2406\r
2407 CpuMpData = GetCpuMpData ();\r
2408\r
2409 //\r
2410 // Check whether caller processor is BSP\r
2411 //\r
2412 MpInitLibWhoAmI (&CallerNumber);\r
2413 if (CallerNumber != CpuMpData->BspNumber) {\r
2414 return EFI_DEVICE_ERROR;\r
2415 }\r
2416\r
2417 if (ProcessorNumber == CpuMpData->BspNumber) {\r
2418 return EFI_INVALID_PARAMETER;\r
2419 }\r
2420\r
2421 if (ProcessorNumber >= CpuMpData->CpuCount) {\r
2422 return EFI_NOT_FOUND;\r
2423 }\r
2424\r
2425 if (!EnableAP) {\r
2426 SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateDisabled);\r
2427 } else {\r
2428 ResetProcessorToIdleState (ProcessorNumber);\r
2429 }\r
2430\r
2431 if (HealthFlag != NULL) {\r
2432 CpuMpData->CpuData[ProcessorNumber].CpuHealthy =\r
2433 (BOOLEAN) ((*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT) != 0);\r
2434 }\r
2435\r
2436 return EFI_SUCCESS;\r
2437}\r
2438\r
2439/**\r
2440 This return the handle number for the calling processor. This service may be\r
2441 called from the BSP and APs.\r
2442\r
2443 @param[out] ProcessorNumber Pointer to the handle number of AP.\r
2444 The range is from 0 to the total number of\r
2445 logical processors minus 1. The total number of\r
2446 logical processors can be retrieved by\r
2447 MpInitLibGetNumberOfProcessors().\r
2448\r
2449 @retval EFI_SUCCESS The current processor handle number was returned\r
2450 in ProcessorNumber.\r
2451 @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL.\r
2452 @retval EFI_NOT_READY MP Initialize Library is not initialized.\r
2453\r
2454**/\r
2455EFI_STATUS\r
2456EFIAPI\r
2457MpInitLibWhoAmI (\r
2458 OUT UINTN *ProcessorNumber\r
2459 )\r
2460{\r
2461 CPU_MP_DATA *CpuMpData;\r
2462\r
2463 if (ProcessorNumber == NULL) {\r
2464 return EFI_INVALID_PARAMETER;\r
2465 }\r
2466\r
2467 CpuMpData = GetCpuMpData ();\r
2468\r
2469 return GetProcessorNumber (CpuMpData, ProcessorNumber);\r
2470}\r
2471\r
2472/**\r
2473 Retrieves the number of logical processor in the platform and the number of\r
2474 those logical processors that are enabled on this boot. This service may only\r
2475 be called from the BSP.\r
2476\r
2477 @param[out] NumberOfProcessors Pointer to the total number of logical\r
2478 processors in the system, including the BSP\r
2479 and disabled APs.\r
2480 @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical\r
2481 processors that exist in system, including\r
2482 the BSP.\r
2483\r
2484 @retval EFI_SUCCESS The number of logical processors and enabled\r
2485 logical processors was retrieved.\r
2486 @retval EFI_DEVICE_ERROR The calling processor is an AP.\r
2487 @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors\r
2488 is NULL.\r
2489 @retval EFI_NOT_READY MP Initialize Library is not initialized.\r
2490\r
2491**/\r
2492EFI_STATUS\r
2493EFIAPI\r
2494MpInitLibGetNumberOfProcessors (\r
2495 OUT UINTN *NumberOfProcessors, OPTIONAL\r
2496 OUT UINTN *NumberOfEnabledProcessors OPTIONAL\r
2497 )\r
2498{\r
2499 CPU_MP_DATA *CpuMpData;\r
2500 UINTN CallerNumber;\r
2501 UINTN ProcessorNumber;\r
2502 UINTN EnabledProcessorNumber;\r
2503 UINTN Index;\r
2504\r
2505 CpuMpData = GetCpuMpData ();\r
2506\r
2507 if ((NumberOfProcessors == NULL) && (NumberOfEnabledProcessors == NULL)) {\r
2508 return EFI_INVALID_PARAMETER;\r
2509 }\r
2510\r
2511 //\r
2512 // Check whether caller processor is BSP\r
2513 //\r
2514 MpInitLibWhoAmI (&CallerNumber);\r
2515 if (CallerNumber != CpuMpData->BspNumber) {\r
2516 return EFI_DEVICE_ERROR;\r
2517 }\r
2518\r
2519 ProcessorNumber = CpuMpData->CpuCount;\r
2520 EnabledProcessorNumber = 0;\r
2521 for (Index = 0; Index < ProcessorNumber; Index++) {\r
2522 if (GetApState (&CpuMpData->CpuData[Index]) != CpuStateDisabled) {\r
2523 EnabledProcessorNumber ++;\r
2524 }\r
2525 }\r
2526\r
2527 if (NumberOfProcessors != NULL) {\r
2528 *NumberOfProcessors = ProcessorNumber;\r
2529 }\r
2530 if (NumberOfEnabledProcessors != NULL) {\r
2531 *NumberOfEnabledProcessors = EnabledProcessorNumber;\r
2532 }\r
2533\r
2534 return EFI_SUCCESS;\r
2535}\r
2536\r
2537\r
2538/**\r
2539 Worker function to execute a caller provided function on all enabled APs.\r
2540\r
2541 @param[in] Procedure A pointer to the function to be run on\r
2542 enabled APs of the system.\r
2543 @param[in] SingleThread If TRUE, then all the enabled APs execute\r
2544 the function specified by Procedure one by\r
2545 one, in ascending order of processor handle\r
2546 number. If FALSE, then all the enabled APs\r
2547 execute the function specified by Procedure\r
2548 simultaneously.\r
2549 @param[in] ExcludeBsp Whether let BSP also trig this task.\r
2550 @param[in] WaitEvent The event created by the caller with CreateEvent()\r
2551 service.\r
2552 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
2553 APs to return from Procedure, either for\r
2554 blocking or non-blocking mode.\r
2555 @param[in] ProcedureArgument The parameter passed into Procedure for\r
2556 all APs.\r
2557 @param[out] FailedCpuList If all APs finish successfully, then its\r
2558 content is set to NULL. If not all APs\r
2559 finish before timeout expires, then its\r
2560 content is set to address of the buffer\r
2561 holding handle numbers of the failed APs.\r
2562\r
2563 @retval EFI_SUCCESS In blocking mode, all APs have finished before\r
2564 the timeout expired.\r
2565 @retval EFI_SUCCESS In non-blocking mode, function has been dispatched\r
2566 to all enabled APs.\r
2567 @retval others Failed to Startup all APs.\r
2568\r
2569**/\r
2570EFI_STATUS\r
2571StartupAllCPUsWorker (\r
2572 IN EFI_AP_PROCEDURE Procedure,\r
2573 IN BOOLEAN SingleThread,\r
2574 IN BOOLEAN ExcludeBsp,\r
2575 IN EFI_EVENT WaitEvent OPTIONAL,\r
2576 IN UINTN TimeoutInMicroseconds,\r
2577 IN VOID *ProcedureArgument OPTIONAL,\r
2578 OUT UINTN **FailedCpuList OPTIONAL\r
2579 )\r
2580{\r
2581 EFI_STATUS Status;\r
2582 CPU_MP_DATA *CpuMpData;\r
2583 UINTN ProcessorCount;\r
2584 UINTN ProcessorNumber;\r
2585 UINTN CallerNumber;\r
2586 CPU_AP_DATA *CpuData;\r
2587 BOOLEAN HasEnabledAp;\r
2588 CPU_STATE ApState;\r
2589\r
2590 CpuMpData = GetCpuMpData ();\r
2591\r
2592 if (FailedCpuList != NULL) {\r
2593 *FailedCpuList = NULL;\r
2594 }\r
2595\r
2596 if (CpuMpData->CpuCount == 1 && ExcludeBsp) {\r
2597 return EFI_NOT_STARTED;\r
2598 }\r
2599\r
2600 if (Procedure == NULL) {\r
2601 return EFI_INVALID_PARAMETER;\r
2602 }\r
2603\r
2604 //\r
2605 // Check whether caller processor is BSP\r
2606 //\r
2607 MpInitLibWhoAmI (&CallerNumber);\r
2608 if (CallerNumber != CpuMpData->BspNumber) {\r
2609 return EFI_DEVICE_ERROR;\r
2610 }\r
2611\r
2612 //\r
2613 // Update AP state\r
2614 //\r
2615 CheckAndUpdateApsStatus ();\r
2616\r
2617 ProcessorCount = CpuMpData->CpuCount;\r
2618 HasEnabledAp = FALSE;\r
2619 //\r
2620 // Check whether all enabled APs are idle.\r
2621 // If any enabled AP is not idle, return EFI_NOT_READY.\r
2622 //\r
2623 for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) {\r
2624 CpuData = &CpuMpData->CpuData[ProcessorNumber];\r
2625 if (ProcessorNumber != CpuMpData->BspNumber) {\r
2626 ApState = GetApState (CpuData);\r
2627 if (ApState != CpuStateDisabled) {\r
2628 HasEnabledAp = TRUE;\r
2629 if (ApState != CpuStateIdle) {\r
2630 //\r
2631 // If any enabled APs are busy, return EFI_NOT_READY.\r
2632 //\r
2633 return EFI_NOT_READY;\r
2634 }\r
2635 }\r
2636 }\r
2637 }\r
2638\r
2639 if (!HasEnabledAp && ExcludeBsp) {\r
2640 //\r
2641 // If no enabled AP exists and not include Bsp to do the procedure, return EFI_NOT_STARTED.\r
2642 //\r
2643 return EFI_NOT_STARTED;\r
2644 }\r
2645\r
2646 CpuMpData->RunningCount = 0;\r
2647 for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) {\r
2648 CpuData = &CpuMpData->CpuData[ProcessorNumber];\r
2649 CpuData->Waiting = FALSE;\r
2650 if (ProcessorNumber != CpuMpData->BspNumber) {\r
2651 if (CpuData->State == CpuStateIdle) {\r
2652 //\r
2653 // Mark this processor as responsible for current calling.\r
2654 //\r
2655 CpuData->Waiting = TRUE;\r
2656 CpuMpData->RunningCount++;\r
2657 }\r
2658 }\r
2659 }\r
2660\r
2661 CpuMpData->Procedure = Procedure;\r
2662 CpuMpData->ProcArguments = ProcedureArgument;\r
2663 CpuMpData->SingleThread = SingleThread;\r
2664 CpuMpData->FinishedCount = 0;\r
2665 CpuMpData->FailedCpuList = FailedCpuList;\r
2666 CpuMpData->ExpectedTime = CalculateTimeout (\r
2667 TimeoutInMicroseconds,\r
2668 &CpuMpData->CurrentTime\r
2669 );\r
2670 CpuMpData->TotalTime = 0;\r
2671 CpuMpData->WaitEvent = WaitEvent;\r
2672\r
2673 if (!SingleThread) {\r
2674 WakeUpAP (CpuMpData, TRUE, 0, Procedure, ProcedureArgument, FALSE);\r
2675 } else {\r
2676 for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) {\r
2677 if (ProcessorNumber == CallerNumber) {\r
2678 continue;\r
2679 }\r
2680 if (CpuMpData->CpuData[ProcessorNumber].Waiting) {\r
2681 WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument, TRUE);\r
2682 break;\r
2683 }\r
2684 }\r
2685 }\r
2686\r
2687 if (!ExcludeBsp) {\r
2688 //\r
2689 // Start BSP.\r
2690 //\r
2691 Procedure (ProcedureArgument);\r
2692 }\r
2693\r
2694 Status = EFI_SUCCESS;\r
2695 if (WaitEvent == NULL) {\r
2696 do {\r
2697 Status = CheckAllAPs ();\r
2698 } while (Status == EFI_NOT_READY);\r
2699 }\r
2700\r
2701 return Status;\r
2702}\r
2703\r
2704/**\r
2705 Worker function to let the caller get one enabled AP to execute a caller-provided\r
2706 function.\r
2707\r
2708 @param[in] Procedure A pointer to the function to be run on\r
2709 enabled APs of the system.\r
2710 @param[in] ProcessorNumber The handle number of the AP.\r
2711 @param[in] WaitEvent The event created by the caller with CreateEvent()\r
2712 service.\r
2713 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
2714 APs to return from Procedure, either for\r
2715 blocking or non-blocking mode.\r
2716 @param[in] ProcedureArgument The parameter passed into Procedure for\r
2717 all APs.\r
2718 @param[out] Finished If AP returns from Procedure before the\r
2719 timeout expires, its content is set to TRUE.\r
2720 Otherwise, the value is set to FALSE.\r
2721\r
2722 @retval EFI_SUCCESS In blocking mode, specified AP finished before\r
2723 the timeout expires.\r
2724 @retval others Failed to Startup AP.\r
2725\r
2726**/\r
2727EFI_STATUS\r
2728StartupThisAPWorker (\r
2729 IN EFI_AP_PROCEDURE Procedure,\r
2730 IN UINTN ProcessorNumber,\r
2731 IN EFI_EVENT WaitEvent OPTIONAL,\r
2732 IN UINTN TimeoutInMicroseconds,\r
2733 IN VOID *ProcedureArgument OPTIONAL,\r
2734 OUT BOOLEAN *Finished OPTIONAL\r
2735 )\r
2736{\r
2737 EFI_STATUS Status;\r
2738 CPU_MP_DATA *CpuMpData;\r
2739 CPU_AP_DATA *CpuData;\r
2740 UINTN CallerNumber;\r
2741\r
2742 CpuMpData = GetCpuMpData ();\r
2743\r
2744 if (Finished != NULL) {\r
2745 *Finished = FALSE;\r
2746 }\r
2747\r
2748 //\r
2749 // Check whether caller processor is BSP\r
2750 //\r
2751 MpInitLibWhoAmI (&CallerNumber);\r
2752 if (CallerNumber != CpuMpData->BspNumber) {\r
2753 return EFI_DEVICE_ERROR;\r
2754 }\r
2755\r
2756 //\r
2757 // Check whether processor with the handle specified by ProcessorNumber exists\r
2758 //\r
2759 if (ProcessorNumber >= CpuMpData->CpuCount) {\r
2760 return EFI_NOT_FOUND;\r
2761 }\r
2762\r
2763 //\r
2764 // Check whether specified processor is BSP\r
2765 //\r
2766 if (ProcessorNumber == CpuMpData->BspNumber) {\r
2767 return EFI_INVALID_PARAMETER;\r
2768 }\r
2769\r
2770 //\r
2771 // Check parameter Procedure\r
2772 //\r
2773 if (Procedure == NULL) {\r
2774 return EFI_INVALID_PARAMETER;\r
2775 }\r
2776\r
2777 //\r
2778 // Update AP state\r
2779 //\r
2780 CheckAndUpdateApsStatus ();\r
2781\r
2782 //\r
2783 // Check whether specified AP is disabled\r
2784 //\r
2785 if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateDisabled) {\r
2786 return EFI_INVALID_PARAMETER;\r
2787 }\r
2788\r
2789 //\r
2790 // If WaitEvent is not NULL, execute in non-blocking mode.\r
2791 // BSP saves data for CheckAPsStatus(), and returns EFI_SUCCESS.\r
2792 // CheckAPsStatus() will check completion and timeout periodically.\r
2793 //\r
2794 CpuData = &CpuMpData->CpuData[ProcessorNumber];\r
2795 CpuData->WaitEvent = WaitEvent;\r
2796 CpuData->Finished = Finished;\r
2797 CpuData->ExpectedTime = CalculateTimeout (TimeoutInMicroseconds, &CpuData->CurrentTime);\r
2798 CpuData->TotalTime = 0;\r
2799\r
2800 WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument, TRUE);\r
2801\r
2802 //\r
2803 // If WaitEvent is NULL, execute in blocking mode.\r
2804 // BSP checks AP's state until it finishes or TimeoutInMicrosecsond expires.\r
2805 //\r
2806 Status = EFI_SUCCESS;\r
2807 if (WaitEvent == NULL) {\r
2808 do {\r
2809 Status = CheckThisAP (ProcessorNumber);\r
2810 } while (Status == EFI_NOT_READY);\r
2811 }\r
2812\r
2813 return Status;\r
2814}\r
2815\r
2816/**\r
2817 Get pointer to CPU MP Data structure from GUIDed HOB.\r
2818\r
2819 @return The pointer to CPU MP Data structure.\r
2820**/\r
2821CPU_MP_DATA *\r
2822GetCpuMpDataFromGuidedHob (\r
2823 VOID\r
2824 )\r
2825{\r
2826 EFI_HOB_GUID_TYPE *GuidHob;\r
2827 VOID *DataInHob;\r
2828 CPU_MP_DATA *CpuMpData;\r
2829\r
2830 CpuMpData = NULL;\r
2831 GuidHob = GetFirstGuidHob (&mCpuInitMpLibHobGuid);\r
2832 if (GuidHob != NULL) {\r
2833 DataInHob = GET_GUID_HOB_DATA (GuidHob);\r
2834 CpuMpData = (CPU_MP_DATA *) (*(UINTN *) DataInHob);\r
2835 }\r
2836 return CpuMpData;\r
2837}\r
2838\r
2839/**\r
2840 This service executes a caller provided function on all enabled CPUs.\r
2841\r
2842 @param[in] Procedure A pointer to the function to be run on\r
2843 enabled APs of the system. See type\r
2844 EFI_AP_PROCEDURE.\r
2845 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
2846 APs to return from Procedure, either for\r
2847 blocking or non-blocking mode. Zero means\r
2848 infinity. TimeoutInMicroseconds is ignored\r
2849 for BSP.\r
2850 @param[in] ProcedureArgument The parameter passed into Procedure for\r
2851 all APs.\r
2852\r
2853 @retval EFI_SUCCESS In blocking mode, all CPUs have finished before\r
2854 the timeout expired.\r
2855 @retval EFI_SUCCESS In non-blocking mode, function has been dispatched\r
2856 to all enabled CPUs.\r
2857 @retval EFI_DEVICE_ERROR Caller processor is AP.\r
2858 @retval EFI_NOT_READY Any enabled APs are busy.\r
2859 @retval EFI_NOT_READY MP Initialize Library is not initialized.\r
2860 @retval EFI_TIMEOUT In blocking mode, the timeout expired before\r
2861 all enabled APs have finished.\r
2862 @retval EFI_INVALID_PARAMETER Procedure is NULL.\r
2863\r
2864**/\r
2865EFI_STATUS\r
2866EFIAPI\r
2867MpInitLibStartupAllCPUs (\r
2868 IN EFI_AP_PROCEDURE Procedure,\r
2869 IN UINTN TimeoutInMicroseconds,\r
2870 IN VOID *ProcedureArgument OPTIONAL\r
2871 )\r
2872{\r
2873 return StartupAllCPUsWorker (\r
2874 Procedure,\r
2875 FALSE,\r
2876 FALSE,\r
2877 NULL,\r
2878 TimeoutInMicroseconds,\r
2879 ProcedureArgument,\r
2880 NULL\r
2881 );\r
2882}\r