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