]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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
a6f799e7 4 Copyright (c) 2016 - 2023, 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
a6f799e7
XY
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
28RELOCATE_AP_LOOP_ENTRY mReservedApLoop;\r
29UINTN mReservedTopOfApStack;\r
30volatile UINT32 mNumberToFinish = 0;\r
facf52ae 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
a6f799e7
XY
382 CPU_MP_DATA *CpuMpData;\r
383 BOOLEAN MwaitSupport;\r
384 UINTN ProcessorNumber;\r
385 UINTN StackStart;\r
4d3314f6 386\r
7367cc6c 387 MpInitLibWhoAmI (&ProcessorNumber);\r
4d3314f6
JF
388 CpuMpData = GetCpuMpData ();\r
389 MwaitSupport = IsMwaitSupport ();\r
33a3408f 390 if (CpuMpData->UseSevEsAPMethod) {\r
a6f799e7
XY
391 //\r
392 // 64-bit AMD processors with SEV-ES\r
393 //\r
33a3408f 394 StackStart = CpuMpData->SevEsAPResetStackStart;\r
a6f799e7
XY
395 mReservedApLoop.AmdSevEntry (\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
a6f799e7
XY
406 //\r
407 // Intel processors (32-bit or 64-bit), 32-bit AMD processors, or 64-bit AMD processors without SEV-ES\r
408 //\r
33a3408f 409 StackStart = mReservedTopOfApStack;\r
a6f799e7
XY
410 mReservedApLoop.GenericEntry (\r
411 MwaitSupport,\r
412 CpuMpData->ApTargetCState,\r
a6f799e7
XY
413 StackStart - ProcessorNumber * AP_SAFE_STACK_SIZE,\r
414 (UINTN)&mNumberToFinish,\r
facf52ae 415 mApPageTable\r
a6f799e7 416 );\r
20da7ca4 417 }\r
053e878b 418\r
4d3314f6
JF
419 //\r
420 // It should never reach here\r
421 //\r
422 ASSERT (FALSE);\r
423}\r
424\r
425/**\r
426 Callback function for ExitBootServices.\r
427\r
428 @param[in] Event Event whose notification function is being invoked.\r
429 @param[in] Context The pointer to the notification function's context,\r
430 which is implementation-dependent.\r
431\r
432**/\r
433VOID\r
434EFIAPI\r
86af2eb8 435MpInitChangeApLoopCallback (\r
053e878b
MK
436 IN EFI_EVENT Event,\r
437 IN VOID *Context\r
4d3314f6
JF
438 )\r
439{\r
053e878b 440 CPU_MP_DATA *CpuMpData;\r
5183fb37 441\r
053e878b
MK
442 CpuMpData = GetCpuMpData ();\r
443 CpuMpData->PmCodeSegment = GetProtectedModeCS ();\r
7b7508ad 444 CpuMpData->Pm16CodeSegment = GetProtectedMode16CS ();\r
053e878b
MK
445 CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);\r
446 mNumberToFinish = CpuMpData->CpuCount - 1;\r
cf4e79e4 447 WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE);\r
9f91cb01
JF
448 while (mNumberToFinish > 0) {\r
449 CpuPause ();\r
450 }\r
20da7ca4 451\r
06544455 452 if (CpuMpData->UseSevEsAPMethod && (CpuMpData->WakeupBuffer != (UINTN)-1)) {\r
20da7ca4
TL
453 //\r
454 // There are APs present. Re-use reserved memory area below 1MB from\r
455 // WakeupBuffer as the area to be used for transitioning to 16-bit mode\r
456 // in support of booting of the AP by an OS.\r
457 //\r
458 CopyMem (\r
053e878b
MK
459 (VOID *)CpuMpData->WakeupBuffer,\r
460 (VOID *)(CpuMpData->AddressMap.RendezvousFunnelAddress +\r
461 CpuMpData->AddressMap.SwitchToRealPM16ModeOffset),\r
20da7ca4
TL
462 CpuMpData->AddressMap.SwitchToRealPM16ModeSize\r
463 );\r
464 }\r
465\r
86af2eb8 466 DEBUG ((DEBUG_INFO, "%a() done!\n", __FUNCTION__));\r
4d3314f6
JF
467}\r
468\r
93ca4c0f
JF
469/**\r
470 Initialize global data for MP support.\r
471\r
472 @param[in] CpuMpData The pointer to CPU MP Data structure.\r
473**/\r
474VOID\r
475InitMpGlobalData (\r
053e878b 476 IN CPU_MP_DATA *CpuMpData\r
93ca4c0f
JF
477 )\r
478{\r
053e878b 479 EFI_STATUS Status;\r
cbcf0cd6 480 EFI_PHYSICAL_ADDRESS Address;\r
053e878b
MK
481 UINTN Index;\r
482 EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc;\r
483 UINTN StackBase;\r
484 CPU_INFO_IN_HOB *CpuInfoInHob;\r
facf52ae
XY
485 MP_ASSEMBLY_ADDRESS_MAP *AddressMap;\r
486 UINT8 *ApLoopFunc;\r
487 UINTN ApLoopFuncSize;\r
e9782e69
XY
488 UINTN StackPages;\r
489 UINTN FuncPages;\r
96378861 490\r
93ca4c0f
JF
491 SaveCpuMpData (CpuMpData);\r
492\r
14e8137c
JF
493 if (CpuMpData->CpuCount == 1) {\r
494 //\r
495 // If only BSP exists, return\r
496 //\r
497 return;\r
498 }\r
499\r
15720a6c
JW
500 if (PcdGetBool (PcdCpuStackGuard)) {\r
501 //\r
502 // One extra page at the bottom of the stack is needed for Guard page.\r
503 //\r
504 if (CpuMpData->CpuApStackSize <= EFI_PAGE_SIZE) {\r
505 DEBUG ((DEBUG_ERROR, "PcdCpuApStackSize is not big enough for Stack Guard!\n"));\r
506 ASSERT (FALSE);\r
507 }\r
508\r
52315261
JW
509 //\r
510 // DXE will reuse stack allocated for APs at PEI phase if it's available.\r
511 // Let's check it here.\r
512 //\r
513 // Note: BSP's stack guard is set at DxeIpl phase. But for the sake of\r
514 // BSP/AP exchange, stack guard for ApTopOfStack of cpu 0 will still be\r
515 // set here.\r
516 //\r
517 CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;\r
15720a6c 518 for (Index = 0; Index < CpuMpData->CpuCount; ++Index) {\r
053e878b 519 if ((CpuInfoInHob != NULL) && (CpuInfoInHob[Index].ApTopOfStack != 0)) {\r
20737c2f 520 StackBase = (UINTN)CpuInfoInHob[Index].ApTopOfStack - CpuMpData->CpuApStackSize;\r
52315261
JW
521 } else {\r
522 StackBase = CpuMpData->Buffer + Index * CpuMpData->CpuApStackSize;\r
523 }\r
15720a6c
JW
524\r
525 Status = gDS->GetMemorySpaceDescriptor (StackBase, &MemDesc);\r
526 ASSERT_EFI_ERROR (Status);\r
527\r
528 Status = gDS->SetMemorySpaceAttributes (\r
529 StackBase,\r
530 EFI_PAGES_TO_SIZE (1),\r
531 MemDesc.Attributes | EFI_MEMORY_RP\r
532 );\r
533 ASSERT_EFI_ERROR (Status);\r
52315261 534\r
053e878b
MK
535 DEBUG ((\r
536 DEBUG_INFO,\r
537 "Stack Guard set at %lx [cpu%lu]!\n",\r
538 (UINT64)StackBase,\r
539 (UINT64)Index\r
540 ));\r
15720a6c
JW
541 }\r
542 }\r
543\r
facf52ae
XY
544 AddressMap = &CpuMpData->AddressMap;\r
545 if (CpuMpData->UseSevEsAPMethod) {\r
546 //\r
547 // 64-bit AMD processors with SEV-ES\r
548 //\r
549 Address = BASE_4GB - 1;\r
0d1ad06c
YX
550 ApLoopFunc = AddressMap->RelocateApLoopFuncAddressAmdSev;\r
551 ApLoopFuncSize = AddressMap->RelocateApLoopFuncSizeAmdSev;\r
facf52ae
XY
552 } else {\r
553 //\r
554 // Intel processors (32-bit or 64-bit), 32-bit AMD processors, or 64-bit AMD processors without SEV-ES\r
555 //\r
556 Address = MAX_ADDRESS;\r
557 ApLoopFunc = AddressMap->RelocateApLoopFuncAddressGeneric;\r
558 ApLoopFuncSize = AddressMap->RelocateApLoopFuncSizeGeneric;\r
559 }\r
560\r
5183fb37 561 //\r
ffd6b0b1
JF
562 // Avoid APs access invalid buffer data which allocated by BootServices,\r
563 // so we will allocate reserved data for AP loop code. We also need to\r
564 // allocate this buffer below 4GB due to APs may be transferred to 32bit\r
565 // protected mode on long mode DXE.\r
5183fb37
JF
566 // Allocating it in advance since memory services are not available in\r
567 // Exit Boot Services callback function.\r
568 //\r
e9782e69
XY
569 // +------------+ (TopOfApStack)\r
570 // | Stack * N |\r
571 // +------------+ (stack base, 4k aligned)\r
572 // | Padding |\r
573 // +------------+\r
574 // | Ap Loop |\r
575 // +------------+ ((low address, 4k-aligned)\r
576 //\r
577\r
578 StackPages = EFI_SIZE_TO_PAGES (CpuMpData->CpuCount * AP_SAFE_STACK_SIZE);\r
facf52ae 579 FuncPages = EFI_SIZE_TO_PAGES (ApLoopFuncSize);\r
bc2288f5 580\r
facf52ae
XY
581 Status = gBS->AllocatePages (\r
582 AllocateMaxAddress,\r
583 EfiReservedMemoryType,\r
584 StackPages + FuncPages,\r
585 &Address\r
586 );\r
587 ASSERT_EFI_ERROR (Status);\r
73ccde8f 588\r
cbcf0cd6
YX
589 //\r
590 // Make sure that the buffer memory is executable if NX protection is enabled\r
591 // for EfiReservedMemoryType.\r
592 //\r
593 // TODO: Check EFI_MEMORY_XP bit set or not once it's available in DXE GCD\r
594 // service.\r
595 //\r
596 Status = gDS->GetMemorySpaceDescriptor (Address, &MemDesc);\r
597 if (!EFI_ERROR (Status)) {\r
598 gDS->SetMemorySpaceAttributes (\r
599 Address,\r
e9782e69 600 EFI_PAGES_TO_SIZE (FuncPages),\r
cbcf0cd6
YX
601 MemDesc.Attributes & (~EFI_MEMORY_XP)\r
602 );\r
73ccde8f
XY
603 }\r
604\r
e9782e69 605 mReservedTopOfApStack = (UINTN)Address + EFI_PAGES_TO_SIZE (StackPages+FuncPages);\r
cbcf0cd6 606 ASSERT ((mReservedTopOfApStack & (UINTN)(CPU_STACK_ALIGNMENT - 1)) == 0);\r
facf52ae
XY
607 mReservedApLoop.Data = (VOID *)(UINTN)Address;\r
608 ASSERT (mReservedApLoop.Data != NULL);\r
609 CopyMem (mReservedApLoop.Data, ApLoopFunc, ApLoopFuncSize);\r
610 if (!CpuMpData->UseSevEsAPMethod) {\r
611 //\r
612 // processors without SEV-ES\r
613 //\r
614 mApPageTable = CreatePageTable (\r
615 (UINTN)Address,\r
616 EFI_PAGES_TO_SIZE (StackPages+FuncPages)\r
617 );\r
618 }\r
5183fb37 619\r
96378861
JF
620 Status = gBS->CreateEvent (\r
621 EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
622 TPL_NOTIFY,\r
623 CheckApsStatus,\r
624 NULL,\r
625 &mCheckAllApsEvent\r
626 );\r
627 ASSERT_EFI_ERROR (Status);\r
628\r
629 //\r
630 // Set timer to check all APs status.\r
631 //\r
632 Status = gBS->SetTimer (\r
633 mCheckAllApsEvent,\r
634 TimerPeriodic,\r
a1c35ff3
HW
635 EFI_TIMER_PERIOD_MICROSECONDS (\r
636 PcdGet32 (PcdCpuApStatusCheckIntervalInMicroSeconds)\r
637 )\r
96378861
JF
638 );\r
639 ASSERT_EFI_ERROR (Status);\r
8677a56a 640\r
4d3314f6
JF
641 Status = gBS->CreateEvent (\r
642 EVT_SIGNAL_EXIT_BOOT_SERVICES,\r
643 TPL_CALLBACK,\r
86af2eb8 644 MpInitChangeApLoopCallback,\r
4d3314f6
JF
645 NULL,\r
646 &mMpInitExitBootServicesEvent\r
647 );\r
648 ASSERT_EFI_ERROR (Status);\r
8677a56a
JF
649\r
650 Status = gBS->CreateEventEx (\r
651 EVT_NOTIFY_SIGNAL,\r
652 TPL_CALLBACK,\r
653 MpInitChangeApLoopCallback,\r
654 NULL,\r
655 &gEfiEventLegacyBootGuid,\r
656 &mLegacyBootEvent\r
657 );\r
658 ASSERT_EFI_ERROR (Status);\r
93ca4c0f 659}\r
3e8ad6bd
JF
660\r
661/**\r
662 This service executes a caller provided function on all enabled APs.\r
663\r
664 @param[in] Procedure A pointer to the function to be run on\r
665 enabled APs of the system. See type\r
666 EFI_AP_PROCEDURE.\r
667 @param[in] SingleThread If TRUE, then all the enabled APs execute\r
668 the function specified by Procedure one by\r
669 one, in ascending order of processor handle\r
670 number. If FALSE, then all the enabled APs\r
671 execute the function specified by Procedure\r
672 simultaneously.\r
673 @param[in] WaitEvent The event created by the caller with CreateEvent()\r
674 service. If it is NULL, then execute in\r
675 blocking mode. BSP waits until all APs finish\r
676 or TimeoutInMicroSeconds expires. If it's\r
677 not NULL, then execute in non-blocking mode.\r
678 BSP requests the function specified by\r
679 Procedure to be started on all the enabled\r
680 APs, and go on executing immediately. If\r
681 all return from Procedure, or TimeoutInMicroSeconds\r
682 expires, this event is signaled. The BSP\r
683 can use the CheckEvent() or WaitForEvent()\r
684 services to check the state of event. Type\r
685 EFI_EVENT is defined in CreateEvent() in\r
686 the Unified Extensible Firmware Interface\r
687 Specification.\r
367284e7 688 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
3e8ad6bd
JF
689 APs to return from Procedure, either for\r
690 blocking or non-blocking mode. Zero means\r
691 infinity. If the timeout expires before\r
692 all APs return from Procedure, then Procedure\r
693 on the failed APs is terminated. All enabled\r
694 APs are available for next function assigned\r
695 by MpInitLibStartupAllAPs() or\r
696 MPInitLibStartupThisAP().\r
697 If the timeout expires in blocking mode,\r
698 BSP returns EFI_TIMEOUT. If the timeout\r
699 expires in non-blocking mode, WaitEvent\r
700 is signaled with SignalEvent().\r
701 @param[in] ProcedureArgument The parameter passed into Procedure for\r
702 all APs.\r
703 @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise,\r
704 if all APs finish successfully, then its\r
705 content is set to NULL. If not all APs\r
706 finish before timeout expires, then its\r
707 content is set to address of the buffer\r
708 holding handle numbers of the failed APs.\r
709 The buffer is allocated by MP Initialization\r
710 library, and it's the caller's responsibility to\r
711 free the buffer with FreePool() service.\r
712 In blocking mode, it is ready for consumption\r
713 when the call returns. In non-blocking mode,\r
714 it is ready when WaitEvent is signaled. The\r
715 list of failed CPU is terminated by\r
716 END_OF_CPU_LIST.\r
717\r
718 @retval EFI_SUCCESS In blocking mode, all APs have finished before\r
719 the timeout expired.\r
720 @retval EFI_SUCCESS In non-blocking mode, function has been dispatched\r
721 to all enabled APs.\r
722 @retval EFI_UNSUPPORTED A non-blocking mode request was made after the\r
723 UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was\r
724 signaled.\r
725 @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not\r
726 supported.\r
727 @retval EFI_DEVICE_ERROR Caller processor is AP.\r
728 @retval EFI_NOT_STARTED No enabled APs exist in the system.\r
729 @retval EFI_NOT_READY Any enabled APs are busy.\r
730 @retval EFI_NOT_READY MP Initialize Library is not initialized.\r
731 @retval EFI_TIMEOUT In blocking mode, the timeout expired before\r
732 all enabled APs have finished.\r
733 @retval EFI_INVALID_PARAMETER Procedure is NULL.\r
734\r
735**/\r
736EFI_STATUS\r
737EFIAPI\r
738MpInitLibStartupAllAPs (\r
053e878b
MK
739 IN EFI_AP_PROCEDURE Procedure,\r
740 IN BOOLEAN SingleThread,\r
741 IN EFI_EVENT WaitEvent OPTIONAL,\r
742 IN UINTN TimeoutInMicroseconds,\r
743 IN VOID *ProcedureArgument OPTIONAL,\r
744 OUT UINTN **FailedCpuList OPTIONAL\r
3e8ad6bd
JF
745 )\r
746{\r
053e878b 747 EFI_STATUS Status;\r
86efe976
JF
748\r
749 //\r
750 // Temporarily stop checkAllApsStatus for avoid resource dead-lock.\r
751 //\r
752 mStopCheckAllApsStatus = TRUE;\r
753\r
ee0c39fa 754 Status = StartupAllCPUsWorker (\r
86efe976
JF
755 Procedure,\r
756 SingleThread,\r
ee0c39fa 757 TRUE,\r
86efe976
JF
758 WaitEvent,\r
759 TimeoutInMicroseconds,\r
760 ProcedureArgument,\r
761 FailedCpuList\r
762 );\r
763\r
764 //\r
765 // Start checkAllApsStatus\r
766 //\r
767 mStopCheckAllApsStatus = FALSE;\r
768\r
769 return Status;\r
3e8ad6bd
JF
770}\r
771\r
772/**\r
773 This service lets the caller get one enabled AP to execute a caller-provided\r
774 function.\r
775\r
776 @param[in] Procedure A pointer to the function to be run on the\r
777 designated AP of the system. See type\r
778 EFI_AP_PROCEDURE.\r
779 @param[in] ProcessorNumber The handle number of the AP. The range is\r
780 from 0 to the total number of logical\r
781 processors minus 1. The total number of\r
782 logical processors can be retrieved by\r
783 MpInitLibGetNumberOfProcessors().\r
784 @param[in] WaitEvent The event created by the caller with CreateEvent()\r
785 service. If it is NULL, then execute in\r
786 blocking mode. BSP waits until this AP finish\r
787 or TimeoutInMicroSeconds expires. If it's\r
788 not NULL, then execute in non-blocking mode.\r
789 BSP requests the function specified by\r
790 Procedure to be started on this AP,\r
791 and go on executing immediately. If this AP\r
792 return from Procedure or TimeoutInMicroSeconds\r
793 expires, this event is signaled. The BSP\r
794 can use the CheckEvent() or WaitForEvent()\r
795 services to check the state of event. Type\r
796 EFI_EVENT is defined in CreateEvent() in\r
797 the Unified Extensible Firmware Interface\r
798 Specification.\r
367284e7 799 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
3e8ad6bd
JF
800 this AP to finish this Procedure, either for\r
801 blocking or non-blocking mode. Zero means\r
802 infinity. If the timeout expires before\r
803 this AP returns from Procedure, then Procedure\r
804 on the AP is terminated. The\r
805 AP is available for next function assigned\r
806 by MpInitLibStartupAllAPs() or\r
807 MpInitLibStartupThisAP().\r
808 If the timeout expires in blocking mode,\r
809 BSP returns EFI_TIMEOUT. If the timeout\r
810 expires in non-blocking mode, WaitEvent\r
811 is signaled with SignalEvent().\r
812 @param[in] ProcedureArgument The parameter passed into Procedure on the\r
813 specified AP.\r
814 @param[out] Finished If NULL, this parameter is ignored. In\r
815 blocking mode, this parameter is ignored.\r
816 In non-blocking mode, if AP returns from\r
817 Procedure before the timeout expires, its\r
818 content is set to TRUE. Otherwise, the\r
819 value is set to FALSE. The caller can\r
820 determine if the AP returned from Procedure\r
821 by evaluating this value.\r
822\r
823 @retval EFI_SUCCESS In blocking mode, specified AP finished before\r
824 the timeout expires.\r
825 @retval EFI_SUCCESS In non-blocking mode, the function has been\r
826 dispatched to specified AP.\r
827 @retval EFI_UNSUPPORTED A non-blocking mode request was made after the\r
828 UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was\r
829 signaled.\r
830 @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not\r
831 supported.\r
832 @retval EFI_DEVICE_ERROR The calling processor is an AP.\r
833 @retval EFI_TIMEOUT In blocking mode, the timeout expired before\r
834 the specified AP has finished.\r
835 @retval EFI_NOT_READY The specified AP is busy.\r
836 @retval EFI_NOT_READY MP Initialize Library is not initialized.\r
837 @retval EFI_NOT_FOUND The processor with the handle specified by\r
838 ProcessorNumber does not exist.\r
839 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.\r
840 @retval EFI_INVALID_PARAMETER Procedure is NULL.\r
841\r
842**/\r
843EFI_STATUS\r
844EFIAPI\r
845MpInitLibStartupThisAP (\r
053e878b
MK
846 IN EFI_AP_PROCEDURE Procedure,\r
847 IN UINTN ProcessorNumber,\r
848 IN EFI_EVENT WaitEvent OPTIONAL,\r
849 IN UINTN TimeoutInMicroseconds,\r
850 IN VOID *ProcedureArgument OPTIONAL,\r
851 OUT BOOLEAN *Finished OPTIONAL\r
3e8ad6bd
JF
852 )\r
853{\r
053e878b 854 EFI_STATUS Status;\r
20ae5774
JF
855\r
856 //\r
857 // temporarily stop checkAllApsStatus for avoid resource dead-lock.\r
858 //\r
859 mStopCheckAllApsStatus = TRUE;\r
860\r
861 Status = StartupThisAPWorker (\r
862 Procedure,\r
863 ProcessorNumber,\r
864 WaitEvent,\r
865 TimeoutInMicroseconds,\r
866 ProcedureArgument,\r
867 Finished\r
868 );\r
869\r
870 mStopCheckAllApsStatus = FALSE;\r
871\r
872 return Status;\r
3e8ad6bd
JF
873}\r
874\r
875/**\r
876 This service switches the requested AP to be the BSP from that point onward.\r
877 This service changes the BSP for all purposes. This call can only be performed\r
878 by the current BSP.\r
879\r
880 @param[in] ProcessorNumber The handle number of AP that is to become the new\r
881 BSP. The range is from 0 to the total number of\r
882 logical processors minus 1. The total number of\r
883 logical processors can be retrieved by\r
884 MpInitLibGetNumberOfProcessors().\r
885 @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an\r
886 enabled AP. Otherwise, it will be disabled.\r
887\r
888 @retval EFI_SUCCESS BSP successfully switched.\r
889 @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to\r
890 this service returning.\r
891 @retval EFI_UNSUPPORTED Switching the BSP is not supported.\r
892 @retval EFI_DEVICE_ERROR The calling processor is an AP.\r
893 @retval EFI_NOT_FOUND The processor with the handle specified by\r
894 ProcessorNumber does not exist.\r
895 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or\r
896 a disabled AP.\r
897 @retval EFI_NOT_READY The specified AP is busy.\r
898 @retval EFI_NOT_READY MP Initialize Library is not initialized.\r
899\r
900**/\r
901EFI_STATUS\r
902EFIAPI\r
903MpInitLibSwitchBSP (\r
053e878b
MK
904 IN UINTN ProcessorNumber,\r
905 IN BOOLEAN EnableOldBSP\r
3e8ad6bd
JF
906 )\r
907{\r
053e878b
MK
908 EFI_STATUS Status;\r
909 EFI_TIMER_ARCH_PROTOCOL *Timer;\r
910 UINT64 TimerPeriod;\r
41be0da5 911\r
8ad05bd2 912 TimerPeriod = 0;\r
b6e45716
JF
913 //\r
914 // Locate Timer Arch Protocol\r
915 //\r
053e878b 916 Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **)&Timer);\r
b6e45716
JF
917 if (EFI_ERROR (Status)) {\r
918 Timer = NULL;\r
919 }\r
920\r
921 if (Timer != NULL) {\r
922 //\r
923 // Save current rate of DXE Timer\r
924 //\r
925 Timer->GetTimerPeriod (Timer, &TimerPeriod);\r
926 //\r
927 // Disable DXE Timer and drain pending interrupts\r
928 //\r
929 Timer->SetTimerPeriod (Timer, 0);\r
930 }\r
41be0da5
JF
931\r
932 Status = SwitchBSPWorker (ProcessorNumber, EnableOldBSP);\r
933\r
b6e45716
JF
934 if (Timer != NULL) {\r
935 //\r
936 // Enable and restore rate of DXE Timer\r
937 //\r
938 Timer->SetTimerPeriod (Timer, TimerPeriod);\r
939 }\r
940\r
41be0da5 941 return Status;\r
3e8ad6bd
JF
942}\r
943\r
944/**\r
945 This service lets the caller enable or disable an AP from this point onward.\r
946 This service may only be called from the BSP.\r
947\r
948 @param[in] ProcessorNumber The handle number of AP.\r
949 The range is from 0 to the total number of\r
950 logical processors minus 1. The total number of\r
951 logical processors can be retrieved by\r
952 MpInitLibGetNumberOfProcessors().\r
953 @param[in] EnableAP Specifies the new state for the processor for\r
954 enabled, FALSE for disabled.\r
955 @param[in] HealthFlag If not NULL, a pointer to a value that specifies\r
956 the new health status of the AP. This flag\r
957 corresponds to StatusFlag defined in\r
958 EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only\r
959 the PROCESSOR_HEALTH_STATUS_BIT is used. All other\r
960 bits are ignored. If it is NULL, this parameter\r
961 is ignored.\r
962\r
963 @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.\r
964 @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed\r
965 prior to this service returning.\r
966 @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported.\r
967 @retval EFI_DEVICE_ERROR The calling processor is an AP.\r
968 @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber\r
969 does not exist.\r
970 @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP.\r
971 @retval EFI_NOT_READY MP Initialize Library is not initialized.\r
972\r
973**/\r
974EFI_STATUS\r
975EFIAPI\r
976MpInitLibEnableDisableAP (\r
053e878b
MK
977 IN UINTN ProcessorNumber,\r
978 IN BOOLEAN EnableAP,\r
979 IN UINT32 *HealthFlag OPTIONAL\r
3e8ad6bd
JF
980 )\r
981{\r
053e878b
MK
982 EFI_STATUS Status;\r
983 BOOLEAN TempStopCheckState;\r
e37109bc
JF
984\r
985 TempStopCheckState = FALSE;\r
986 //\r
987 // temporarily stop checkAllAPsStatus for initialize parameters.\r
988 //\r
989 if (!mStopCheckAllApsStatus) {\r
990 mStopCheckAllApsStatus = TRUE;\r
991 TempStopCheckState = TRUE;\r
992 }\r
993\r
994 Status = EnableDisableApWorker (ProcessorNumber, EnableAP, HealthFlag);\r
995\r
996 if (TempStopCheckState) {\r
997 mStopCheckAllApsStatus = FALSE;\r
998 }\r
999\r
1000 return Status;\r
3e8ad6bd 1001}\r
c788c2b1
SF
1002\r
1003/**\r
1004 This funtion will try to invoke platform specific microcode shadow logic to\r
1005 relocate microcode update patches into memory.\r
1006\r
4ac82ea1 1007 @param[in, out] CpuMpData The pointer to CPU MP Data structure.\r
c788c2b1
SF
1008\r
1009 @retval EFI_SUCCESS Shadow microcode success.\r
1010 @retval EFI_OUT_OF_RESOURCES No enough resource to complete the operation.\r
1011 @retval EFI_UNSUPPORTED Can't find platform specific microcode shadow\r
1012 PPI/Protocol.\r
1013**/\r
1014EFI_STATUS\r
1015PlatformShadowMicrocode (\r
053e878b 1016 IN OUT CPU_MP_DATA *CpuMpData\r
c788c2b1
SF
1017 )\r
1018{\r
1019 //\r
1020 // There is no DXE version of platform shadow microcode protocol so far.\r
1021 // A platform which only uses DxeMpInitLib instance could only supports\r
1022 // the PCD based microcode shadowing.\r
1023 //\r
1024 return EFI_UNSUPPORTED;\r
1025}\r