]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
UefiCpuPkg: Has APs in 64 bit long-mode before booting to OS.
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / DxeMpLib.c
CommitLineData
3e8ad6bd
JF
1/** @file\r
2 MP initialize support functions for DXE phase.\r
3\r
7bda8c64 4 Copyright (c) 2016 - 2022, Intel Corporation. All rights reserved.<BR>\r
0acd8697 5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
3e8ad6bd
JF
6\r
7**/\r
8\r
9#include "MpLib.h"\r
96378861
JF
10\r
11#include <Library/UefiLib.h>\r
12#include <Library/UefiBootServicesTableLib.h>\r
43c9fdcc 13#include <Library/DebugAgentLib.h>\r
15720a6c 14#include <Library/DxeServicesTableLib.h>\r
a89f558d 15#include <Library/CcExitLib.h>\r
7b7508ad
TL
16#include <Register/Amd/Fam17Msr.h>\r
17#include <Register/Amd/Ghcb.h>\r
96378861 18\r
b6e45716
JF
19#include <Protocol/Timer.h>\r
20\r
053e878b 21#define AP_SAFE_STACK_SIZE 128\r
96378861 22\r
053e878b
MK
23CPU_MP_DATA *mCpuMpData = NULL;\r
24EFI_EVENT mCheckAllApsEvent = NULL;\r
25EFI_EVENT mMpInitExitBootServicesEvent = NULL;\r
26EFI_EVENT mLegacyBootEvent = NULL;\r
27volatile BOOLEAN mStopCheckAllApsStatus = TRUE;\r
28VOID *mReservedApLoopFunc = NULL;\r
29UINTN mReservedTopOfApStack;\r
30volatile UINT32 mNumberToFinish = 0;\r
73ccde8f 31UINTN mApPageTable;\r
93ca4c0f 32\r
dbc22a17
TL
33//\r
34// Begin wakeup buffer allocation below 0x88000\r
35//\r
053e878b 36STATIC EFI_PHYSICAL_ADDRESS mSevEsDxeWakeupBuffer = 0x88000;\r
dbc22a17 37\r
43c9fdcc
JF
38/**\r
39 Enable Debug Agent to support source debugging on AP function.\r
40\r
41**/\r
42VOID\r
43EnableDebugAgent (\r
44 VOID\r
45 )\r
46{\r
47 //\r
48 // Initialize Debug Agent to support source level debug in DXE phase\r
49 //\r
50 InitializeDebugAgent (DEBUG_AGENT_INIT_DXE_AP, NULL, NULL);\r
51}\r
52\r
93ca4c0f
JF
53/**\r
54 Get the pointer to CPU MP Data structure.\r
55\r
56 @return The pointer to CPU MP Data structure.\r
57**/\r
58CPU_MP_DATA *\r
59GetCpuMpData (\r
60 VOID\r
61 )\r
62{\r
63 ASSERT (mCpuMpData != NULL);\r
64 return mCpuMpData;\r
65}\r
66\r
67/**\r
68 Save the pointer to CPU MP Data structure.\r
69\r
70 @param[in] CpuMpData The pointer to CPU MP Data structure will be saved.\r
71**/\r
72VOID\r
73SaveCpuMpData (\r
053e878b 74 IN CPU_MP_DATA *CpuMpData\r
93ca4c0f
JF
75 )\r
76{\r
77 mCpuMpData = CpuMpData;\r
78}\r
79\r
96378861 80/**\r
e4ff6349 81 Get available system memory below 0x88000 by specified size.\r
ed66e0e3 82\r
a6b3d753 83 @param[in] WakeupBufferSize Wakeup buffer size required\r
3ed4e502 84\r
a6b3d753
SZ
85 @retval other Return wakeup buffer address below 1MB.\r
86 @retval -1 Cannot find free memory below 1MB.\r
ed66e0e3 87**/\r
a6b3d753
SZ
88UINTN\r
89GetWakeupBuffer (\r
053e878b 90 IN UINTN WakeupBufferSize\r
ed66e0e3
JF
91 )\r
92{\r
053e878b
MK
93 EFI_STATUS Status;\r
94 EFI_PHYSICAL_ADDRESS StartAddress;\r
95 EFI_MEMORY_TYPE MemoryType;\r
20da7ca4 96\r
06544455
TL
97 if (ConfidentialComputingGuestHas (CCAttrAmdSevEs) &&\r
98 !ConfidentialComputingGuestHas (CCAttrAmdSevSnp))\r
99 {\r
100 //\r
101 // An SEV-ES-only guest requires the memory to be reserved. SEV-SNP, which\r
102 // is also considered SEV-ES, uses a different AP startup method, though,\r
103 // which does not have the same requirement.\r
104 //\r
20da7ca4
TL
105 MemoryType = EfiReservedMemoryType;\r
106 } else {\r
107 MemoryType = EfiBootServicesData;\r
108 }\r
a6b3d753 109\r
e4ff6349
ED
110 //\r
111 // Try to allocate buffer below 1M for waking vector.\r
112 // LegacyBios driver only reports warning when page allocation in range\r
113 // [0x60000, 0x88000) fails.\r
114 // This library is consumed by CpuDxe driver to produce CPU Arch protocol.\r
115 // LagacyBios driver depends on CPU Arch protocol which guarantees below\r
116 // allocation runs earlier than LegacyBios driver.\r
117 //\r
b95908e0 118 if (ConfidentialComputingGuestHas (CCAttrAmdSevEs)) {\r
dbc22a17
TL
119 //\r
120 // SEV-ES Wakeup buffer should be under 0x88000 and under any previous one\r
121 //\r
122 StartAddress = mSevEsDxeWakeupBuffer;\r
123 } else {\r
124 StartAddress = 0x88000;\r
125 }\r
053e878b 126\r
a6b3d753
SZ
127 Status = gBS->AllocatePages (\r
128 AllocateMaxAddress,\r
20da7ca4 129 MemoryType,\r
a6b3d753
SZ
130 EFI_SIZE_TO_PAGES (WakeupBufferSize),\r
131 &StartAddress\r
132 );\r
133 ASSERT_EFI_ERROR (Status);\r
e4ff6349 134 if (EFI_ERROR (Status)) {\r
053e878b 135 StartAddress = (EFI_PHYSICAL_ADDRESS)-1;\r
b95908e0 136 } else if (ConfidentialComputingGuestHas (CCAttrAmdSevEs)) {\r
dbc22a17
TL
137 //\r
138 // Next SEV-ES wakeup buffer allocation must be below this allocation\r
139 //\r
140 mSevEsDxeWakeupBuffer = StartAddress;\r
3ed4e502 141 }\r
e4ff6349 142\r
053e878b
MK
143 DEBUG ((\r
144 DEBUG_INFO,\r
145 "WakeupBufferStart = %x, WakeupBufferSize = %x\n",\r
146 (UINTN)StartAddress,\r
147 WakeupBufferSize\r
148 ));\r
e4ff6349 149\r
053e878b 150 return (UINTN)StartAddress;\r
ed66e0e3
JF
151}\r
152\r
f32bfe6d
JW
153/**\r
154 Get available EfiBootServicesCode memory below 4GB by specified size.\r
155\r
156 This buffer is required to safely transfer AP from real address mode to\r
157 protected mode or long mode, due to the fact that the buffer returned by\r
158 GetWakeupBuffer() may be marked as non-executable.\r
159\r
160 @param[in] BufferSize Wakeup transition buffer size.\r
161\r
162 @retval other Return wakeup transition buffer address below 4GB.\r
163 @retval 0 Cannot find free memory below 4GB.\r
164**/\r
165UINTN\r
54aeed7e 166AllocateCodeBuffer (\r
053e878b 167 IN UINTN BufferSize\r
f32bfe6d
JW
168 )\r
169{\r
053e878b
MK
170 EFI_STATUS Status;\r
171 EFI_PHYSICAL_ADDRESS StartAddress;\r
f32bfe6d
JW
172\r
173 StartAddress = BASE_4GB - 1;\r
053e878b
MK
174 Status = gBS->AllocatePages (\r
175 AllocateMaxAddress,\r
176 EfiBootServicesCode,\r
177 EFI_SIZE_TO_PAGES (BufferSize),\r
178 &StartAddress\r
179 );\r
f32bfe6d
JW
180 if (EFI_ERROR (Status)) {\r
181 StartAddress = 0;\r
182 }\r
183\r
184 return (UINTN)StartAddress;\r
185}\r
186\r
7b7508ad
TL
187/**\r
188 Return the address of the SEV-ES AP jump table.\r
189\r
190 This buffer is required in order for an SEV-ES guest to transition from\r
191 UEFI into an OS.\r
192\r
193 @return Return SEV-ES AP jump table buffer\r
194**/\r
195UINTN\r
196GetSevEsAPMemory (\r
197 VOID\r
198 )\r
199{\r
20da7ca4
TL
200 EFI_STATUS Status;\r
201 EFI_PHYSICAL_ADDRESS StartAddress;\r
202 MSR_SEV_ES_GHCB_REGISTER Msr;\r
203 GHCB *Ghcb;\r
1b0db1ec 204 BOOLEAN InterruptState;\r
7b7508ad
TL
205\r
206 //\r
207 // Allocate 1 page for AP jump table page\r
208 //\r
209 StartAddress = BASE_4GB - 1;\r
053e878b
MK
210 Status = gBS->AllocatePages (\r
211 AllocateMaxAddress,\r
212 EfiReservedMemoryType,\r
213 1,\r
214 &StartAddress\r
215 );\r
7b7508ad
TL
216 ASSERT_EFI_ERROR (Status);\r
217\r
053e878b 218 DEBUG ((DEBUG_INFO, "Dxe: SevEsAPMemory = %lx\n", (UINTN)StartAddress));\r
7b7508ad 219\r
20da7ca4
TL
220 //\r
221 // Save the SevEsAPMemory as the AP jump table.\r
222 //\r
223 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
053e878b 224 Ghcb = Msr.Ghcb;\r
20da7ca4 225\r
765ba5bf
MX
226 CcExitVmgInit (Ghcb, &InterruptState);\r
227 CcExitVmgExit (Ghcb, SVM_EXIT_AP_JUMP_TABLE, 0, (UINT64)(UINTN)StartAddress);\r
228 CcExitVmgDone (Ghcb, InterruptState);\r
20da7ca4 229\r
053e878b 230 return (UINTN)StartAddress;\r
7b7508ad
TL
231}\r
232\r
96378861
JF
233/**\r
234 Checks APs status and updates APs status if needed.\r
235\r
236**/\r
237VOID\r
238CheckAndUpdateApsStatus (\r
239 VOID\r
240 )\r
241{\r
053e878b
MK
242 UINTN ProcessorNumber;\r
243 EFI_STATUS Status;\r
244 CPU_MP_DATA *CpuMpData;\r
08085f08
JF
245\r
246 CpuMpData = GetCpuMpData ();\r
247\r
248 //\r
249 // First, check whether pending StartupAllAPs() exists.\r
250 //\r
251 if (CpuMpData->WaitEvent != NULL) {\r
08085f08
JF
252 Status = CheckAllAPs ();\r
253 //\r
254 // If all APs finish for StartupAllAPs(), signal the WaitEvent for it.\r
255 //\r
256 if (Status != EFI_NOT_READY) {\r
053e878b 257 Status = gBS->SignalEvent (CpuMpData->WaitEvent);\r
08085f08
JF
258 CpuMpData->WaitEvent = NULL;\r
259 }\r
260 }\r
261\r
262 //\r
263 // Second, check whether pending StartupThisAPs() callings exist.\r
264 //\r
265 for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) {\r
08085f08
JF
266 if (CpuMpData->CpuData[ProcessorNumber].WaitEvent == NULL) {\r
267 continue;\r
268 }\r
269\r
270 Status = CheckThisAP (ProcessorNumber);\r
271\r
272 if (Status != EFI_NOT_READY) {\r
273 gBS->SignalEvent (CpuMpData->CpuData[ProcessorNumber].WaitEvent);\r
053e878b 274 CpuMpData->CpuData[ProcessorNumber].WaitEvent = NULL;\r
08085f08
JF
275 }\r
276 }\r
96378861
JF
277}\r
278\r
279/**\r
280 Checks APs' status periodically.\r
281\r
438f1766 282 This function is triggered by timer periodically to check the\r
96378861
JF
283 state of APs for StartupAllAPs() and StartupThisAP() executed\r
284 in non-blocking mode.\r
285\r
286 @param[in] Event Event triggered.\r
287 @param[in] Context Parameter passed with the event.\r
288\r
289**/\r
290VOID\r
291EFIAPI\r
292CheckApsStatus (\r
053e878b
MK
293 IN EFI_EVENT Event,\r
294 IN VOID *Context\r
96378861
JF
295 )\r
296{\r
297 //\r
298 // If CheckApsStatus() is not stopped, otherwise return immediately.\r
299 //\r
300 if (!mStopCheckAllApsStatus) {\r
301 CheckAndUpdateApsStatus ();\r
302 }\r
303}\r
ed66e0e3 304\r
7b7508ad
TL
305/**\r
306 Get Protected mode code segment with 16-bit default addressing\r
307 from current GDT table.\r
308\r
309 @return Protected mode 16-bit code segment value.\r
310**/\r
311UINT16\r
312GetProtectedMode16CS (\r
313 VOID\r
314 )\r
315{\r
316 IA32_DESCRIPTOR GdtrDesc;\r
317 IA32_SEGMENT_DESCRIPTOR *GdtEntry;\r
318 UINTN GdtEntryCount;\r
319 UINT16 Index;\r
320\r
053e878b 321 Index = (UINT16)-1;\r
7b7508ad
TL
322 AsmReadGdtr (&GdtrDesc);\r
323 GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
053e878b 324 GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;\r
7b7508ad
TL
325 for (Index = 0; Index < GdtEntryCount; Index++) {\r
326 if (GdtEntry->Bits.L == 0) {\r
053e878b 327 if ((GdtEntry->Bits.Type > 8) && (GdtEntry->Bits.DB == 0)) {\r
7b7508ad
TL
328 break;\r
329 }\r
330 }\r
053e878b 331\r
7b7508ad
TL
332 GdtEntry++;\r
333 }\r
053e878b 334\r
7b7508ad
TL
335 ASSERT (Index != GdtEntryCount);\r
336 return Index * 8;\r
337}\r
338\r
4d3314f6
JF
339/**\r
340 Get Protected mode code segment from current GDT table.\r
341\r
b31c1ad1 342 @return Protected mode code segment value.\r
4d3314f6
JF
343**/\r
344UINT16\r
345GetProtectedModeCS (\r
346 VOID\r
347 )\r
348{\r
349 IA32_DESCRIPTOR GdtrDesc;\r
350 IA32_SEGMENT_DESCRIPTOR *GdtEntry;\r
351 UINTN GdtEntryCount;\r
352 UINT16 Index;\r
353\r
4d3314f6
JF
354 AsmReadGdtr (&GdtrDesc);\r
355 GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
053e878b 356 GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;\r
4d3314f6
JF
357 for (Index = 0; Index < GdtEntryCount; Index++) {\r
358 if (GdtEntry->Bits.L == 0) {\r
053e878b 359 if ((GdtEntry->Bits.Type > 8) && (GdtEntry->Bits.DB == 1)) {\r
4d3314f6
JF
360 break;\r
361 }\r
362 }\r
053e878b 363\r
4d3314f6
JF
364 GdtEntry++;\r
365 }\r
053e878b 366\r
37fba7c2 367 ASSERT (Index != GdtEntryCount);\r
4d3314f6
JF
368 return Index * 8;\r
369}\r
370\r
371/**\r
372 Do sync on APs.\r
373\r
374 @param[in, out] Buffer Pointer to private data buffer.\r
375**/\r
376VOID\r
377EFIAPI\r
378RelocateApLoop (\r
379 IN OUT VOID *Buffer\r
380 )\r
381{\r
7bda8c64
YX
382 CPU_MP_DATA *CpuMpData;\r
383 BOOLEAN MwaitSupport;\r
384 ASM_RELOCATE_AP_LOOP AsmRelocateApLoopFunc;\r
385 ASM_RELOCATE_AP_LOOP_AMD AsmRelocateApLoopFuncAmd;\r
386 UINTN ProcessorNumber;\r
387 UINTN StackStart;\r
4d3314f6 388\r
7367cc6c 389 MpInitLibWhoAmI (&ProcessorNumber);\r
4d3314f6
JF
390 CpuMpData = GetCpuMpData ();\r
391 MwaitSupport = IsMwaitSupport ();\r
7bda8c64
YX
392 if (StandardSignatureIsAuthenticAMD ()) {\r
393 StackStart = CpuMpData->UseSevEsAPMethod ? CpuMpData->SevEsAPResetStackStart : mReservedTopOfApStack;\r
394 AsmRelocateApLoopFuncAmd = (ASM_RELOCATE_AP_LOOP_AMD)(UINTN)mReservedApLoopFunc;\r
395 AsmRelocateApLoopFuncAmd (\r
396 MwaitSupport,\r
397 CpuMpData->ApTargetCState,\r
398 CpuMpData->PmCodeSegment,\r
399 StackStart - ProcessorNumber * AP_SAFE_STACK_SIZE,\r
400 (UINTN)&mNumberToFinish,\r
401 CpuMpData->Pm16CodeSegment,\r
402 CpuMpData->SevEsAPBuffer,\r
403 CpuMpData->WakeupBuffer\r
404 );\r
20da7ca4 405 } else {\r
7bda8c64
YX
406 StackStart = mReservedTopOfApStack;\r
407 AsmRelocateApLoopFunc = (ASM_RELOCATE_AP_LOOP)(UINTN)mReservedApLoopFunc;\r
408 AsmRelocateApLoopFunc (\r
409 MwaitSupport,\r
410 CpuMpData->ApTargetCState,\r
7bda8c64
YX
411 StackStart - ProcessorNumber * AP_SAFE_STACK_SIZE,\r
412 (UINTN)&mNumberToFinish,\r
73ccde8f 413 mApPageTable\r
7bda8c64 414 );\r
20da7ca4 415 }\r
053e878b 416\r
4d3314f6
JF
417 //\r
418 // It should never reach here\r
419 //\r
420 ASSERT (FALSE);\r
421}\r
422\r
423/**\r
424 Callback function for ExitBootServices.\r
425\r
426 @param[in] Event Event whose notification function is being invoked.\r
427 @param[in] Context The pointer to the notification function's context,\r
428 which is implementation-dependent.\r
429\r
430**/\r
431VOID\r
432EFIAPI\r
86af2eb8 433MpInitChangeApLoopCallback (\r
053e878b
MK
434 IN EFI_EVENT Event,\r
435 IN VOID *Context\r
4d3314f6
JF
436 )\r
437{\r
053e878b 438 CPU_MP_DATA *CpuMpData;\r
5183fb37 439\r
053e878b
MK
440 CpuMpData = GetCpuMpData ();\r
441 CpuMpData->PmCodeSegment = GetProtectedModeCS ();\r
7b7508ad 442 CpuMpData->Pm16CodeSegment = GetProtectedMode16CS ();\r
053e878b
MK
443 CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);\r
444 mNumberToFinish = CpuMpData->CpuCount - 1;\r
cf4e79e4 445 WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE);\r
9f91cb01
JF
446 while (mNumberToFinish > 0) {\r
447 CpuPause ();\r
448 }\r
20da7ca4 449\r
06544455 450 if (CpuMpData->UseSevEsAPMethod && (CpuMpData->WakeupBuffer != (UINTN)-1)) {\r
20da7ca4
TL
451 //\r
452 // There are APs present. Re-use reserved memory area below 1MB from\r
453 // WakeupBuffer as the area to be used for transitioning to 16-bit mode\r
454 // in support of booting of the AP by an OS.\r
455 //\r
456 CopyMem (\r
053e878b
MK
457 (VOID *)CpuMpData->WakeupBuffer,\r
458 (VOID *)(CpuMpData->AddressMap.RendezvousFunnelAddress +\r
459 CpuMpData->AddressMap.SwitchToRealPM16ModeOffset),\r
20da7ca4
TL
460 CpuMpData->AddressMap.SwitchToRealPM16ModeSize\r
461 );\r
462 }\r
463\r
86af2eb8 464 DEBUG ((DEBUG_INFO, "%a() done!\n", __FUNCTION__));\r
4d3314f6
JF
465}\r
466\r
93ca4c0f
JF
467/**\r
468 Initialize global data for MP support.\r
469\r
470 @param[in] CpuMpData The pointer to CPU MP Data structure.\r
471**/\r
472VOID\r
473InitMpGlobalData (\r
053e878b 474 IN CPU_MP_DATA *CpuMpData\r
93ca4c0f
JF
475 )\r
476{\r
053e878b 477 EFI_STATUS Status;\r
053e878b
MK
478 UINTN ApSafeBufferSize;\r
479 UINTN Index;\r
480 EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc;\r
481 UINTN StackBase;\r
482 CPU_INFO_IN_HOB *CpuInfoInHob;\r
96378861 483\r
93ca4c0f
JF
484 SaveCpuMpData (CpuMpData);\r
485\r
14e8137c
JF
486 if (CpuMpData->CpuCount == 1) {\r
487 //\r
488 // If only BSP exists, return\r
489 //\r
490 return;\r
491 }\r
492\r
15720a6c
JW
493 if (PcdGetBool (PcdCpuStackGuard)) {\r
494 //\r
495 // One extra page at the bottom of the stack is needed for Guard page.\r
496 //\r
497 if (CpuMpData->CpuApStackSize <= EFI_PAGE_SIZE) {\r
498 DEBUG ((DEBUG_ERROR, "PcdCpuApStackSize is not big enough for Stack Guard!\n"));\r
499 ASSERT (FALSE);\r
500 }\r
501\r
52315261
JW
502 //\r
503 // DXE will reuse stack allocated for APs at PEI phase if it's available.\r
504 // Let's check it here.\r
505 //\r
506 // Note: BSP's stack guard is set at DxeIpl phase. But for the sake of\r
507 // BSP/AP exchange, stack guard for ApTopOfStack of cpu 0 will still be\r
508 // set here.\r
509 //\r
510 CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;\r
15720a6c 511 for (Index = 0; Index < CpuMpData->CpuCount; ++Index) {\r
053e878b 512 if ((CpuInfoInHob != NULL) && (CpuInfoInHob[Index].ApTopOfStack != 0)) {\r
20737c2f 513 StackBase = (UINTN)CpuInfoInHob[Index].ApTopOfStack - CpuMpData->CpuApStackSize;\r
52315261
JW
514 } else {\r
515 StackBase = CpuMpData->Buffer + Index * CpuMpData->CpuApStackSize;\r
516 }\r
15720a6c
JW
517\r
518 Status = gDS->GetMemorySpaceDescriptor (StackBase, &MemDesc);\r
519 ASSERT_EFI_ERROR (Status);\r
520\r
521 Status = gDS->SetMemorySpaceAttributes (\r
522 StackBase,\r
523 EFI_PAGES_TO_SIZE (1),\r
524 MemDesc.Attributes | EFI_MEMORY_RP\r
525 );\r
526 ASSERT_EFI_ERROR (Status);\r
52315261 527\r
053e878b
MK
528 DEBUG ((\r
529 DEBUG_INFO,\r
530 "Stack Guard set at %lx [cpu%lu]!\n",\r
531 (UINT64)StackBase,\r
532 (UINT64)Index\r
533 ));\r
15720a6c
JW
534 }\r
535 }\r
536\r
5183fb37 537 //\r
ffd6b0b1
JF
538 // Avoid APs access invalid buffer data which allocated by BootServices,\r
539 // so we will allocate reserved data for AP loop code. We also need to\r
540 // allocate this buffer below 4GB due to APs may be transferred to 32bit\r
541 // protected mode on long mode DXE.\r
5183fb37
JF
542 // Allocating it in advance since memory services are not available in\r
543 // Exit Boot Services callback function.\r
544 //\r
73ccde8f
XY
545 // +------------+\r
546 // | Ap Loop |\r
547 // +------------+\r
548 // | Stack * N |\r
549 // +------------+ (low address)\r
bc2288f5 550 //\r
053e878b
MK
551 ApSafeBufferSize = EFI_PAGES_TO_SIZE (\r
552 EFI_SIZE_TO_PAGES (\r
553 CpuMpData->CpuCount * AP_SAFE_STACK_SIZE\r
73ccde8f 554 + CpuMpData->AddressMap.RelocateApLoopFuncSize\r
053e878b
MK
555 )\r
556 );\r
bc2288f5 557\r
73ccde8f
XY
558 mReservedTopOfApStack = (UINTN)AllocateReservedPages (EFI_SIZE_TO_PAGES (ApSafeBufferSize));\r
559 ASSERT (mReservedTopOfApStack != 0);\r
bf2786dc 560 ASSERT ((mReservedTopOfApStack & (UINTN)(CPU_STACK_ALIGNMENT - 1)) == 0);\r
73ccde8f
XY
561 ASSERT ((AP_SAFE_STACK_SIZE & (CPU_STACK_ALIGNMENT - 1)) == 0);\r
562\r
563 mReservedApLoopFunc = (VOID *)(mReservedTopOfApStack + CpuMpData->CpuCount * AP_SAFE_STACK_SIZE);\r
564 if (StandardSignatureIsAuthenticAMD ()) {\r
565 CopyMem (\r
566 mReservedApLoopFunc,\r
567 CpuMpData->AddressMap.RelocateApLoopFuncAddressAmd,\r
568 CpuMpData->AddressMap.RelocateApLoopFuncSizeAmd\r
569 );\r
570 } else {\r
571 CopyMem (\r
572 mReservedApLoopFunc,\r
573 CpuMpData->AddressMap.RelocateApLoopFuncAddress,\r
574 CpuMpData->AddressMap.RelocateApLoopFuncSize\r
575 );\r
576\r
577 mApPageTable = CreatePageTable (\r
578 mReservedTopOfApStack,\r
579 ApSafeBufferSize\r
580 );\r
581 }\r
582\r
583 mReservedTopOfApStack += CpuMpData->CpuCount * AP_SAFE_STACK_SIZE;\r
5183fb37 584\r
96378861
JF
585 Status = gBS->CreateEvent (\r
586 EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
587 TPL_NOTIFY,\r
588 CheckApsStatus,\r
589 NULL,\r
590 &mCheckAllApsEvent\r
591 );\r
592 ASSERT_EFI_ERROR (Status);\r
593\r
594 //\r
595 // Set timer to check all APs status.\r
596 //\r
597 Status = gBS->SetTimer (\r
598 mCheckAllApsEvent,\r
599 TimerPeriodic,\r
a1c35ff3
HW
600 EFI_TIMER_PERIOD_MICROSECONDS (\r
601 PcdGet32 (PcdCpuApStatusCheckIntervalInMicroSeconds)\r
602 )\r
96378861
JF
603 );\r
604 ASSERT_EFI_ERROR (Status);\r
8677a56a 605\r
4d3314f6
JF
606 Status = gBS->CreateEvent (\r
607 EVT_SIGNAL_EXIT_BOOT_SERVICES,\r
608 TPL_CALLBACK,\r
86af2eb8 609 MpInitChangeApLoopCallback,\r
4d3314f6
JF
610 NULL,\r
611 &mMpInitExitBootServicesEvent\r
612 );\r
613 ASSERT_EFI_ERROR (Status);\r
8677a56a
JF
614\r
615 Status = gBS->CreateEventEx (\r
616 EVT_NOTIFY_SIGNAL,\r
617 TPL_CALLBACK,\r
618 MpInitChangeApLoopCallback,\r
619 NULL,\r
620 &gEfiEventLegacyBootGuid,\r
621 &mLegacyBootEvent\r
622 );\r
623 ASSERT_EFI_ERROR (Status);\r
93ca4c0f 624}\r
3e8ad6bd
JF
625\r
626/**\r
627 This service executes a caller provided function on all enabled APs.\r
628\r
629 @param[in] Procedure A pointer to the function to be run on\r
630 enabled APs of the system. See type\r
631 EFI_AP_PROCEDURE.\r
632 @param[in] SingleThread If TRUE, then all the enabled APs execute\r
633 the function specified by Procedure one by\r
634 one, in ascending order of processor handle\r
635 number. If FALSE, then all the enabled APs\r
636 execute the function specified by Procedure\r
637 simultaneously.\r
638 @param[in] WaitEvent The event created by the caller with CreateEvent()\r
639 service. If it is NULL, then execute in\r
640 blocking mode. BSP waits until all APs finish\r
641 or TimeoutInMicroSeconds expires. If it's\r
642 not NULL, then execute in non-blocking mode.\r
643 BSP requests the function specified by\r
644 Procedure to be started on all the enabled\r
645 APs, and go on executing immediately. If\r
646 all return from Procedure, or TimeoutInMicroSeconds\r
647 expires, this event is signaled. The BSP\r
648 can use the CheckEvent() or WaitForEvent()\r
649 services to check the state of event. Type\r
650 EFI_EVENT is defined in CreateEvent() in\r
651 the Unified Extensible Firmware Interface\r
652 Specification.\r
367284e7 653 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
3e8ad6bd
JF
654 APs to return from Procedure, either for\r
655 blocking or non-blocking mode. Zero means\r
656 infinity. If the timeout expires before\r
657 all APs return from Procedure, then Procedure\r
658 on the failed APs is terminated. All enabled\r
659 APs are available for next function assigned\r
660 by MpInitLibStartupAllAPs() or\r
661 MPInitLibStartupThisAP().\r
662 If the timeout expires in blocking mode,\r
663 BSP returns EFI_TIMEOUT. If the timeout\r
664 expires in non-blocking mode, WaitEvent\r
665 is signaled with SignalEvent().\r
666 @param[in] ProcedureArgument The parameter passed into Procedure for\r
667 all APs.\r
668 @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise,\r
669 if all APs finish successfully, then its\r
670 content is set to NULL. If not all APs\r
671 finish before timeout expires, then its\r
672 content is set to address of the buffer\r
673 holding handle numbers of the failed APs.\r
674 The buffer is allocated by MP Initialization\r
675 library, and it's the caller's responsibility to\r
676 free the buffer with FreePool() service.\r
677 In blocking mode, it is ready for consumption\r
678 when the call returns. In non-blocking mode,\r
679 it is ready when WaitEvent is signaled. The\r
680 list of failed CPU is terminated by\r
681 END_OF_CPU_LIST.\r
682\r
683 @retval EFI_SUCCESS In blocking mode, all APs have finished before\r
684 the timeout expired.\r
685 @retval EFI_SUCCESS In non-blocking mode, function has been dispatched\r
686 to all enabled APs.\r
687 @retval EFI_UNSUPPORTED A non-blocking mode request was made after the\r
688 UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was\r
689 signaled.\r
690 @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not\r
691 supported.\r
692 @retval EFI_DEVICE_ERROR Caller processor is AP.\r
693 @retval EFI_NOT_STARTED No enabled APs exist in the system.\r
694 @retval EFI_NOT_READY Any enabled APs are busy.\r
695 @retval EFI_NOT_READY MP Initialize Library is not initialized.\r
696 @retval EFI_TIMEOUT In blocking mode, the timeout expired before\r
697 all enabled APs have finished.\r
698 @retval EFI_INVALID_PARAMETER Procedure is NULL.\r
699\r
700**/\r
701EFI_STATUS\r
702EFIAPI\r
703MpInitLibStartupAllAPs (\r
053e878b
MK
704 IN EFI_AP_PROCEDURE Procedure,\r
705 IN BOOLEAN SingleThread,\r
706 IN EFI_EVENT WaitEvent OPTIONAL,\r
707 IN UINTN TimeoutInMicroseconds,\r
708 IN VOID *ProcedureArgument OPTIONAL,\r
709 OUT UINTN **FailedCpuList OPTIONAL\r
3e8ad6bd
JF
710 )\r
711{\r
053e878b 712 EFI_STATUS Status;\r
86efe976
JF
713\r
714 //\r
715 // Temporarily stop checkAllApsStatus for avoid resource dead-lock.\r
716 //\r
717 mStopCheckAllApsStatus = TRUE;\r
718\r
ee0c39fa 719 Status = StartupAllCPUsWorker (\r
86efe976
JF
720 Procedure,\r
721 SingleThread,\r
ee0c39fa 722 TRUE,\r
86efe976
JF
723 WaitEvent,\r
724 TimeoutInMicroseconds,\r
725 ProcedureArgument,\r
726 FailedCpuList\r
727 );\r
728\r
729 //\r
730 // Start checkAllApsStatus\r
731 //\r
732 mStopCheckAllApsStatus = FALSE;\r
733\r
734 return Status;\r
3e8ad6bd
JF
735}\r
736\r
737/**\r
738 This service lets the caller get one enabled AP to execute a caller-provided\r
739 function.\r
740\r
741 @param[in] Procedure A pointer to the function to be run on the\r
742 designated AP of the system. See type\r
743 EFI_AP_PROCEDURE.\r
744 @param[in] ProcessorNumber The handle number of the AP. The range is\r
745 from 0 to the total number of logical\r
746 processors minus 1. The total number of\r
747 logical processors can be retrieved by\r
748 MpInitLibGetNumberOfProcessors().\r
749 @param[in] WaitEvent The event created by the caller with CreateEvent()\r
750 service. If it is NULL, then execute in\r
751 blocking mode. BSP waits until this AP finish\r
752 or TimeoutInMicroSeconds expires. If it's\r
753 not NULL, then execute in non-blocking mode.\r
754 BSP requests the function specified by\r
755 Procedure to be started on this AP,\r
756 and go on executing immediately. If this AP\r
757 return from Procedure or TimeoutInMicroSeconds\r
758 expires, this event is signaled. The BSP\r
759 can use the CheckEvent() or WaitForEvent()\r
760 services to check the state of event. Type\r
761 EFI_EVENT is defined in CreateEvent() in\r
762 the Unified Extensible Firmware Interface\r
763 Specification.\r
367284e7 764 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
3e8ad6bd
JF
765 this AP to finish this Procedure, either for\r
766 blocking or non-blocking mode. Zero means\r
767 infinity. If the timeout expires before\r
768 this AP returns from Procedure, then Procedure\r
769 on the AP is terminated. The\r
770 AP is available for next function assigned\r
771 by MpInitLibStartupAllAPs() or\r
772 MpInitLibStartupThisAP().\r
773 If the timeout expires in blocking mode,\r
774 BSP returns EFI_TIMEOUT. If the timeout\r
775 expires in non-blocking mode, WaitEvent\r
776 is signaled with SignalEvent().\r
777 @param[in] ProcedureArgument The parameter passed into Procedure on the\r
778 specified AP.\r
779 @param[out] Finished If NULL, this parameter is ignored. In\r
780 blocking mode, this parameter is ignored.\r
781 In non-blocking mode, if AP returns from\r
782 Procedure before the timeout expires, its\r
783 content is set to TRUE. Otherwise, the\r
784 value is set to FALSE. The caller can\r
785 determine if the AP returned from Procedure\r
786 by evaluating this value.\r
787\r
788 @retval EFI_SUCCESS In blocking mode, specified AP finished before\r
789 the timeout expires.\r
790 @retval EFI_SUCCESS In non-blocking mode, the function has been\r
791 dispatched to specified AP.\r
792 @retval EFI_UNSUPPORTED A non-blocking mode request was made after the\r
793 UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was\r
794 signaled.\r
795 @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not\r
796 supported.\r
797 @retval EFI_DEVICE_ERROR The calling processor is an AP.\r
798 @retval EFI_TIMEOUT In blocking mode, the timeout expired before\r
799 the specified AP has finished.\r
800 @retval EFI_NOT_READY The specified AP is busy.\r
801 @retval EFI_NOT_READY MP Initialize Library is not initialized.\r
802 @retval EFI_NOT_FOUND The processor with the handle specified by\r
803 ProcessorNumber does not exist.\r
804 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.\r
805 @retval EFI_INVALID_PARAMETER Procedure is NULL.\r
806\r
807**/\r
808EFI_STATUS\r
809EFIAPI\r
810MpInitLibStartupThisAP (\r
053e878b
MK
811 IN EFI_AP_PROCEDURE Procedure,\r
812 IN UINTN ProcessorNumber,\r
813 IN EFI_EVENT WaitEvent OPTIONAL,\r
814 IN UINTN TimeoutInMicroseconds,\r
815 IN VOID *ProcedureArgument OPTIONAL,\r
816 OUT BOOLEAN *Finished OPTIONAL\r
3e8ad6bd
JF
817 )\r
818{\r
053e878b 819 EFI_STATUS Status;\r
20ae5774
JF
820\r
821 //\r
822 // temporarily stop checkAllApsStatus for avoid resource dead-lock.\r
823 //\r
824 mStopCheckAllApsStatus = TRUE;\r
825\r
826 Status = StartupThisAPWorker (\r
827 Procedure,\r
828 ProcessorNumber,\r
829 WaitEvent,\r
830 TimeoutInMicroseconds,\r
831 ProcedureArgument,\r
832 Finished\r
833 );\r
834\r
835 mStopCheckAllApsStatus = FALSE;\r
836\r
837 return Status;\r
3e8ad6bd
JF
838}\r
839\r
840/**\r
841 This service switches the requested AP to be the BSP from that point onward.\r
842 This service changes the BSP for all purposes. This call can only be performed\r
843 by the current BSP.\r
844\r
845 @param[in] ProcessorNumber The handle number of AP that is to become the new\r
846 BSP. The range is from 0 to the total number of\r
847 logical processors minus 1. The total number of\r
848 logical processors can be retrieved by\r
849 MpInitLibGetNumberOfProcessors().\r
850 @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an\r
851 enabled AP. Otherwise, it will be disabled.\r
852\r
853 @retval EFI_SUCCESS BSP successfully switched.\r
854 @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to\r
855 this service returning.\r
856 @retval EFI_UNSUPPORTED Switching the BSP is not supported.\r
857 @retval EFI_DEVICE_ERROR The calling processor is an AP.\r
858 @retval EFI_NOT_FOUND The processor with the handle specified by\r
859 ProcessorNumber does not exist.\r
860 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or\r
861 a disabled AP.\r
862 @retval EFI_NOT_READY The specified AP is busy.\r
863 @retval EFI_NOT_READY MP Initialize Library is not initialized.\r
864\r
865**/\r
866EFI_STATUS\r
867EFIAPI\r
868MpInitLibSwitchBSP (\r
053e878b
MK
869 IN UINTN ProcessorNumber,\r
870 IN BOOLEAN EnableOldBSP\r
3e8ad6bd
JF
871 )\r
872{\r
053e878b
MK
873 EFI_STATUS Status;\r
874 EFI_TIMER_ARCH_PROTOCOL *Timer;\r
875 UINT64 TimerPeriod;\r
41be0da5 876\r
8ad05bd2 877 TimerPeriod = 0;\r
b6e45716
JF
878 //\r
879 // Locate Timer Arch Protocol\r
880 //\r
053e878b 881 Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **)&Timer);\r
b6e45716
JF
882 if (EFI_ERROR (Status)) {\r
883 Timer = NULL;\r
884 }\r
885\r
886 if (Timer != NULL) {\r
887 //\r
888 // Save current rate of DXE Timer\r
889 //\r
890 Timer->GetTimerPeriod (Timer, &TimerPeriod);\r
891 //\r
892 // Disable DXE Timer and drain pending interrupts\r
893 //\r
894 Timer->SetTimerPeriod (Timer, 0);\r
895 }\r
41be0da5
JF
896\r
897 Status = SwitchBSPWorker (ProcessorNumber, EnableOldBSP);\r
898\r
b6e45716
JF
899 if (Timer != NULL) {\r
900 //\r
901 // Enable and restore rate of DXE Timer\r
902 //\r
903 Timer->SetTimerPeriod (Timer, TimerPeriod);\r
904 }\r
905\r
41be0da5 906 return Status;\r
3e8ad6bd
JF
907}\r
908\r
909/**\r
910 This service lets the caller enable or disable an AP from this point onward.\r
911 This service may only be called from the BSP.\r
912\r
913 @param[in] ProcessorNumber The handle number of AP.\r
914 The range is from 0 to the total number of\r
915 logical processors minus 1. The total number of\r
916 logical processors can be retrieved by\r
917 MpInitLibGetNumberOfProcessors().\r
918 @param[in] EnableAP Specifies the new state for the processor for\r
919 enabled, FALSE for disabled.\r
920 @param[in] HealthFlag If not NULL, a pointer to a value that specifies\r
921 the new health status of the AP. This flag\r
922 corresponds to StatusFlag defined in\r
923 EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only\r
924 the PROCESSOR_HEALTH_STATUS_BIT is used. All other\r
925 bits are ignored. If it is NULL, this parameter\r
926 is ignored.\r
927\r
928 @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.\r
929 @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed\r
930 prior to this service returning.\r
931 @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported.\r
932 @retval EFI_DEVICE_ERROR The calling processor is an AP.\r
933 @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber\r
934 does not exist.\r
935 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP.\r
936 @retval EFI_NOT_READY MP Initialize Library is not initialized.\r
937\r
938**/\r
939EFI_STATUS\r
940EFIAPI\r
941MpInitLibEnableDisableAP (\r
053e878b
MK
942 IN UINTN ProcessorNumber,\r
943 IN BOOLEAN EnableAP,\r
944 IN UINT32 *HealthFlag OPTIONAL\r
3e8ad6bd
JF
945 )\r
946{\r
053e878b
MK
947 EFI_STATUS Status;\r
948 BOOLEAN TempStopCheckState;\r
e37109bc
JF
949\r
950 TempStopCheckState = FALSE;\r
951 //\r
952 // temporarily stop checkAllAPsStatus for initialize parameters.\r
953 //\r
954 if (!mStopCheckAllApsStatus) {\r
955 mStopCheckAllApsStatus = TRUE;\r
956 TempStopCheckState = TRUE;\r
957 }\r
958\r
959 Status = EnableDisableApWorker (ProcessorNumber, EnableAP, HealthFlag);\r
960\r
961 if (TempStopCheckState) {\r
962 mStopCheckAllApsStatus = FALSE;\r
963 }\r
964\r
965 return Status;\r
3e8ad6bd 966}\r
c788c2b1
SF
967\r
968/**\r
969 This funtion will try to invoke platform specific microcode shadow logic to\r
970 relocate microcode update patches into memory.\r
971\r
4ac82ea1 972 @param[in, out] CpuMpData The pointer to CPU MP Data structure.\r
c788c2b1
SF
973\r
974 @retval EFI_SUCCESS Shadow microcode success.\r
975 @retval EFI_OUT_OF_RESOURCES No enough resource to complete the operation.\r
976 @retval EFI_UNSUPPORTED Can't find platform specific microcode shadow\r
977 PPI/Protocol.\r
978**/\r
979EFI_STATUS\r
980PlatformShadowMicrocode (\r
053e878b 981 IN OUT CPU_MP_DATA *CpuMpData\r
c788c2b1
SF
982 )\r
983{\r
984 //\r
985 // There is no DXE version of platform shadow microcode protocol so far.\r
986 // A platform which only uses DxeMpInitLib instance could only supports\r
987 // the PCD based microcode shadowing.\r
988 //\r
989 return EFI_UNSUPPORTED;\r
990}\r