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