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