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