]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / DxeMpLib.c
... / ...
CommitLineData
1/** @file\r
2 MP initialize support functions for DXE phase.\r
3\r
4 Copyright (c) 2016 - 2023, Intel Corporation. All rights reserved.<BR>\r
5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
6\r
7**/\r
8\r
9#include "MpLib.h"\r
10\r
11#include <Library/UefiLib.h>\r
12#include <Library/UefiBootServicesTableLib.h>\r
13#include <Library/DebugAgentLib.h>\r
14#include <Library/DxeServicesTableLib.h>\r
15#include <Library/CcExitLib.h>\r
16#include <Register/Amd/Fam17Msr.h>\r
17#include <Register/Amd/Ghcb.h>\r
18\r
19#include <Protocol/Timer.h>\r
20\r
21#define AP_SAFE_STACK_SIZE 128\r
22\r
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
31UINTN mApPageTable;\r
32\r
33//\r
34// Begin wakeup buffer allocation below 0x88000\r
35//\r
36STATIC EFI_PHYSICAL_ADDRESS mSevEsDxeWakeupBuffer = 0x88000;\r
37\r
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
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
74 IN CPU_MP_DATA *CpuMpData\r
75 )\r
76{\r
77 mCpuMpData = CpuMpData;\r
78}\r
79\r
80/**\r
81 Get available system memory below 0x88000 by specified size.\r
82\r
83 @param[in] WakeupBufferSize Wakeup buffer size required\r
84\r
85 @retval other Return wakeup buffer address below 1MB.\r
86 @retval -1 Cannot find free memory below 1MB.\r
87**/\r
88UINTN\r
89GetWakeupBuffer (\r
90 IN UINTN WakeupBufferSize\r
91 )\r
92{\r
93 EFI_STATUS Status;\r
94 EFI_PHYSICAL_ADDRESS StartAddress;\r
95 EFI_MEMORY_TYPE MemoryType;\r
96\r
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
105 MemoryType = EfiReservedMemoryType;\r
106 } else {\r
107 MemoryType = EfiBootServicesData;\r
108 }\r
109\r
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
118 if (ConfidentialComputingGuestHas (CCAttrAmdSevEs)) {\r
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
126\r
127 Status = gBS->AllocatePages (\r
128 AllocateMaxAddress,\r
129 MemoryType,\r
130 EFI_SIZE_TO_PAGES (WakeupBufferSize),\r
131 &StartAddress\r
132 );\r
133 ASSERT_EFI_ERROR (Status);\r
134 if (EFI_ERROR (Status)) {\r
135 StartAddress = (EFI_PHYSICAL_ADDRESS)-1;\r
136 } else if (ConfidentialComputingGuestHas (CCAttrAmdSevEs)) {\r
137 //\r
138 // Next SEV-ES wakeup buffer allocation must be below this allocation\r
139 //\r
140 mSevEsDxeWakeupBuffer = StartAddress;\r
141 }\r
142\r
143 DEBUG ((\r
144 DEBUG_INFO,\r
145 "WakeupBufferStart = %x, WakeupBufferSize = %x\n",\r
146 (UINTN)StartAddress,\r
147 WakeupBufferSize\r
148 ));\r
149\r
150 return (UINTN)StartAddress;\r
151}\r
152\r
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
166AllocateCodeBuffer (\r
167 IN UINTN BufferSize\r
168 )\r
169{\r
170 EFI_STATUS Status;\r
171 EFI_PHYSICAL_ADDRESS StartAddress;\r
172\r
173 StartAddress = BASE_4GB - 1;\r
174 Status = gBS->AllocatePages (\r
175 AllocateMaxAddress,\r
176 EfiBootServicesCode,\r
177 EFI_SIZE_TO_PAGES (BufferSize),\r
178 &StartAddress\r
179 );\r
180 if (EFI_ERROR (Status)) {\r
181 StartAddress = 0;\r
182 }\r
183\r
184 return (UINTN)StartAddress;\r
185}\r
186\r
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
200 EFI_STATUS Status;\r
201 EFI_PHYSICAL_ADDRESS StartAddress;\r
202 MSR_SEV_ES_GHCB_REGISTER Msr;\r
203 GHCB *Ghcb;\r
204 BOOLEAN InterruptState;\r
205\r
206 //\r
207 // Allocate 1 page for AP jump table page\r
208 //\r
209 StartAddress = BASE_4GB - 1;\r
210 Status = gBS->AllocatePages (\r
211 AllocateMaxAddress,\r
212 EfiReservedMemoryType,\r
213 1,\r
214 &StartAddress\r
215 );\r
216 ASSERT_EFI_ERROR (Status);\r
217\r
218 DEBUG ((DEBUG_INFO, "Dxe: SevEsAPMemory = %lx\n", (UINTN)StartAddress));\r
219\r
220 //\r
221 // Save the SevEsAPMemory as the AP jump table.\r
222 //\r
223 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
224 Ghcb = Msr.Ghcb;\r
225\r
226 CcExitVmgInit (Ghcb, &InterruptState);\r
227 CcExitVmgExit (Ghcb, SVM_EXIT_AP_JUMP_TABLE, 0, (UINT64)(UINTN)StartAddress);\r
228 CcExitVmgDone (Ghcb, InterruptState);\r
229\r
230 return (UINTN)StartAddress;\r
231}\r
232\r
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
242 UINTN ProcessorNumber;\r
243 EFI_STATUS Status;\r
244 CPU_MP_DATA *CpuMpData;\r
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
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
257 Status = gBS->SignalEvent (CpuMpData->WaitEvent);\r
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
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
274 CpuMpData->CpuData[ProcessorNumber].WaitEvent = NULL;\r
275 }\r
276 }\r
277}\r
278\r
279/**\r
280 Checks APs' status periodically.\r
281\r
282 This function is triggered by timer periodically to check the\r
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
293 IN EFI_EVENT Event,\r
294 IN VOID *Context\r
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
304\r
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
321 Index = (UINT16)-1;\r
322 AsmReadGdtr (&GdtrDesc);\r
323 GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
324 GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;\r
325 for (Index = 0; Index < GdtEntryCount; Index++) {\r
326 if (GdtEntry->Bits.L == 0) {\r
327 if ((GdtEntry->Bits.Type > 8) && (GdtEntry->Bits.DB == 0)) {\r
328 break;\r
329 }\r
330 }\r
331\r
332 GdtEntry++;\r
333 }\r
334\r
335 ASSERT (Index != GdtEntryCount);\r
336 return Index * 8;\r
337}\r
338\r
339/**\r
340 Get Protected mode code segment from current GDT table.\r
341\r
342 @return Protected mode code segment value.\r
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
354 AsmReadGdtr (&GdtrDesc);\r
355 GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
356 GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;\r
357 for (Index = 0; Index < GdtEntryCount; Index++) {\r
358 if (GdtEntry->Bits.L == 0) {\r
359 if ((GdtEntry->Bits.Type > 8) && (GdtEntry->Bits.DB == 1)) {\r
360 break;\r
361 }\r
362 }\r
363\r
364 GdtEntry++;\r
365 }\r
366\r
367 ASSERT (Index != GdtEntryCount);\r
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
382 CPU_MP_DATA *CpuMpData;\r
383 BOOLEAN MwaitSupport;\r
384 UINTN ProcessorNumber;\r
385 UINTN StackStart;\r
386\r
387 MpInitLibWhoAmI (&ProcessorNumber);\r
388 CpuMpData = GetCpuMpData ();\r
389 MwaitSupport = IsMwaitSupport ();\r
390 if (CpuMpData->UseSevEsAPMethod) {\r
391 //\r
392 // 64-bit AMD processors with SEV-ES\r
393 //\r
394 StackStart = CpuMpData->SevEsAPResetStackStart;\r
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
405 } else {\r
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
409 StackStart = mReservedTopOfApStack;\r
410 mReservedApLoop.GenericEntry (\r
411 MwaitSupport,\r
412 CpuMpData->ApTargetCState,\r
413 StackStart - ProcessorNumber * AP_SAFE_STACK_SIZE,\r
414 (UINTN)&mNumberToFinish,\r
415 mApPageTable\r
416 );\r
417 }\r
418\r
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
435MpInitChangeApLoopCallback (\r
436 IN EFI_EVENT Event,\r
437 IN VOID *Context\r
438 )\r
439{\r
440 CPU_MP_DATA *CpuMpData;\r
441\r
442 CpuMpData = GetCpuMpData ();\r
443 CpuMpData->PmCodeSegment = GetProtectedModeCS ();\r
444 CpuMpData->Pm16CodeSegment = GetProtectedMode16CS ();\r
445 CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);\r
446 mNumberToFinish = CpuMpData->CpuCount - 1;\r
447 WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE);\r
448 while (mNumberToFinish > 0) {\r
449 CpuPause ();\r
450 }\r
451\r
452 if (CpuMpData->UseSevEsAPMethod && (CpuMpData->WakeupBuffer != (UINTN)-1)) {\r
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
459 (VOID *)CpuMpData->WakeupBuffer,\r
460 (VOID *)(CpuMpData->AddressMap.RendezvousFunnelAddress +\r
461 CpuMpData->AddressMap.SwitchToRealPM16ModeOffset),\r
462 CpuMpData->AddressMap.SwitchToRealPM16ModeSize\r
463 );\r
464 }\r
465\r
466 DEBUG ((DEBUG_INFO, "%a() done!\n", __FUNCTION__));\r
467}\r
468\r
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
476 IN CPU_MP_DATA *CpuMpData\r
477 )\r
478{\r
479 EFI_STATUS Status;\r
480 EFI_PHYSICAL_ADDRESS Address;\r
481 UINTN Index;\r
482 EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc;\r
483 UINTN StackBase;\r
484 CPU_INFO_IN_HOB *CpuInfoInHob;\r
485 MP_ASSEMBLY_ADDRESS_MAP *AddressMap;\r
486 UINT8 *ApLoopFunc;\r
487 UINTN ApLoopFuncSize;\r
488 UINTN StackPages;\r
489 UINTN FuncPages;\r
490\r
491 SaveCpuMpData (CpuMpData);\r
492\r
493 if (CpuMpData->CpuCount == 1) {\r
494 //\r
495 // If only BSP exists, return\r
496 //\r
497 return;\r
498 }\r
499\r
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
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
518 for (Index = 0; Index < CpuMpData->CpuCount; ++Index) {\r
519 if ((CpuInfoInHob != NULL) && (CpuInfoInHob[Index].ApTopOfStack != 0)) {\r
520 StackBase = (UINTN)CpuInfoInHob[Index].ApTopOfStack - CpuMpData->CpuApStackSize;\r
521 } else {\r
522 StackBase = CpuMpData->Buffer + Index * CpuMpData->CpuApStackSize;\r
523 }\r
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
534\r
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
541 }\r
542 }\r
543\r
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
550 ApLoopFunc = AddressMap->RelocateApLoopFuncAddressAmdSev;\r
551 ApLoopFuncSize = AddressMap->RelocateApLoopFuncSizeAmdSev;\r
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
561 //\r
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
566 // Allocating it in advance since memory services are not available in\r
567 // Exit Boot Services callback function.\r
568 //\r
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
579 FuncPages = EFI_SIZE_TO_PAGES (ApLoopFuncSize);\r
580\r
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
588\r
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
600 EFI_PAGES_TO_SIZE (FuncPages),\r
601 MemDesc.Attributes & (~EFI_MEMORY_XP)\r
602 );\r
603 }\r
604\r
605 mReservedTopOfApStack = (UINTN)Address + EFI_PAGES_TO_SIZE (StackPages+FuncPages);\r
606 ASSERT ((mReservedTopOfApStack & (UINTN)(CPU_STACK_ALIGNMENT - 1)) == 0);\r
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
619\r
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
635 EFI_TIMER_PERIOD_MICROSECONDS (\r
636 PcdGet32 (PcdCpuApStatusCheckIntervalInMicroSeconds)\r
637 )\r
638 );\r
639 ASSERT_EFI_ERROR (Status);\r
640\r
641 Status = gBS->CreateEvent (\r
642 EVT_SIGNAL_EXIT_BOOT_SERVICES,\r
643 TPL_CALLBACK,\r
644 MpInitChangeApLoopCallback,\r
645 NULL,\r
646 &mMpInitExitBootServicesEvent\r
647 );\r
648 ASSERT_EFI_ERROR (Status);\r
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
659}\r
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
688 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
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
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
745 )\r
746{\r
747 EFI_STATUS Status;\r
748\r
749 //\r
750 // Temporarily stop checkAllApsStatus for avoid resource dead-lock.\r
751 //\r
752 mStopCheckAllApsStatus = TRUE;\r
753\r
754 Status = StartupAllCPUsWorker (\r
755 Procedure,\r
756 SingleThread,\r
757 TRUE,\r
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
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
799 @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
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
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
852 )\r
853{\r
854 EFI_STATUS Status;\r
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
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
904 IN UINTN ProcessorNumber,\r
905 IN BOOLEAN EnableOldBSP\r
906 )\r
907{\r
908 EFI_STATUS Status;\r
909 EFI_TIMER_ARCH_PROTOCOL *Timer;\r
910 UINT64 TimerPeriod;\r
911\r
912 TimerPeriod = 0;\r
913 //\r
914 // Locate Timer Arch Protocol\r
915 //\r
916 Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **)&Timer);\r
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
931\r
932 Status = SwitchBSPWorker (ProcessorNumber, EnableOldBSP);\r
933\r
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
941 return Status;\r
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
977 IN UINTN ProcessorNumber,\r
978 IN BOOLEAN EnableAP,\r
979 IN UINT32 *HealthFlag OPTIONAL\r
980 )\r
981{\r
982 EFI_STATUS Status;\r
983 BOOLEAN TempStopCheckState;\r
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
1001}\r
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
1007 @param[in, out] CpuMpData The pointer to CPU MP Data structure.\r
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
1016 IN OUT CPU_MP_DATA *CpuMpData\r
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