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