]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - UefiCpuPkg/CpuMpPei/CpuMpPei.c
UefiCpuPkg/ExceptionLib: Import PeiCpuExceptionHandlerLib module
[mirror_edk2.git] / UefiCpuPkg / CpuMpPei / CpuMpPei.c
... / ...
CommitLineData
1/** @file\r
2 CPU PEI Module installs CPU Multiple Processor PPI.\r
3\r
4 Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>\r
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "CpuMpPei.h"\r
16\r
17//\r
18// Global Descriptor Table (GDT)\r
19//\r
20GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT mGdtEntries[] = {\r
21/* selector { Global Segment Descriptor } */\r
22/* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //null descriptor\r
23/* 0x08 */ {{0xffff, 0, 0, 0x2, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear data segment descriptor\r
24/* 0x10 */ {{0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear code segment descriptor\r
25/* 0x18 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor\r
26/* 0x20 */ {{0xffff, 0, 0, 0xa, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system code segment descriptor\r
27/* 0x28 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor\r
28/* 0x30 */ {{0xffff, 0, 0, 0x2, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor\r
29/* 0x38 */ {{0xffff, 0, 0, 0xa, 1, 0, 1, 0xf, 0, 1, 0, 1, 0}}, //system code segment descriptor\r
30/* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor\r
31};\r
32\r
33//\r
34// IA32 Gdt register\r
35//\r
36GLOBAL_REMOVE_IF_UNREFERENCED IA32_DESCRIPTOR mGdt = {\r
37 sizeof (mGdtEntries) - 1,\r
38 (UINTN) mGdtEntries\r
39 };\r
40\r
41GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_NOTIFY_DESCRIPTOR mNotifyList = {\r
42 (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
43 &gEfiEndOfPeiSignalPpiGuid,\r
44 CpuMpEndOfPeiCallback\r
45};\r
46\r
47/**\r
48 Sort the APIC ID of all processors.\r
49\r
50 This function sorts the APIC ID of all processors so that processor number is\r
51 assigned in the ascending order of APIC ID which eases MP debugging.\r
52\r
53 @param PeiCpuMpData Pointer to PEI CPU MP Data\r
54**/\r
55VOID\r
56SortApicId (\r
57 IN PEI_CPU_MP_DATA *PeiCpuMpData\r
58 )\r
59{\r
60 UINTN Index1;\r
61 UINTN Index2;\r
62 UINTN Index3;\r
63 UINT32 ApicId;\r
64 PEI_CPU_DATA CpuData;\r
65 UINT32 ApCount;\r
66\r
67 ApCount = PeiCpuMpData->CpuCount - 1;\r
68\r
69 if (ApCount != 0) {\r
70 for (Index1 = 0; Index1 < ApCount; Index1++) {\r
71 Index3 = Index1;\r
72 //\r
73 // Sort key is the hardware default APIC ID\r
74 //\r
75 ApicId = PeiCpuMpData->CpuData[Index1].ApicId;\r
76 for (Index2 = Index1 + 1; Index2 <= ApCount; Index2++) {\r
77 if (ApicId > PeiCpuMpData->CpuData[Index2].ApicId) {\r
78 Index3 = Index2;\r
79 ApicId = PeiCpuMpData->CpuData[Index2].ApicId;\r
80 }\r
81 }\r
82 if (Index3 != Index1) {\r
83 CopyMem (&CpuData, &PeiCpuMpData->CpuData[Index3], sizeof (PEI_CPU_DATA));\r
84 CopyMem (\r
85 &PeiCpuMpData->CpuData[Index3],\r
86 &PeiCpuMpData->CpuData[Index1],\r
87 sizeof (PEI_CPU_DATA)\r
88 );\r
89 CopyMem (&PeiCpuMpData->CpuData[Index1], &CpuData, sizeof (PEI_CPU_DATA));\r
90 }\r
91 }\r
92\r
93 //\r
94 // Get the processor number for the BSP\r
95 //\r
96 ApicId = GetInitialApicId ();\r
97 for (Index1 = 0; Index1 < PeiCpuMpData->CpuCount; Index1++) {\r
98 if (PeiCpuMpData->CpuData[Index1].ApicId == ApicId) {\r
99 PeiCpuMpData->BspNumber = (UINT32) Index1;\r
100 break;\r
101 }\r
102 }\r
103 }\r
104}\r
105\r
106/**\r
107 Enable x2APIC mode on APs.\r
108\r
109 @param Buffer Pointer to private data buffer.\r
110**/\r
111VOID\r
112EFIAPI\r
113ApFuncEnableX2Apic (\r
114 IN OUT VOID *Buffer\r
115 )\r
116{\r
117 SetApicMode (LOCAL_APIC_MODE_X2APIC);\r
118}\r
119\r
120/**\r
121 Get AP loop mode.\r
122\r
123 @param MonitorFilterSize Returns the largest monitor-line size in bytes.\r
124\r
125 @return The AP loop mode.\r
126**/\r
127UINT8\r
128GetApLoopMode (\r
129 OUT UINT16 *MonitorFilterSize\r
130 )\r
131{\r
132 UINT8 ApLoopMode;\r
133 UINT32 RegEbx;\r
134 UINT32 RegEcx;\r
135 UINT32 RegEdx;\r
136\r
137 ASSERT (MonitorFilterSize != NULL);\r
138\r
139 ApLoopMode = PcdGet8 (PcdCpuApLoopMode);\r
140 ASSERT (ApLoopMode >= ApInHltLoop && ApLoopMode <= ApInRunLoop);\r
141 if (ApLoopMode == ApInMwaitLoop) {\r
142 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, &RegEcx, NULL);\r
143 if ((RegEcx & BIT3) == 0) {\r
144 //\r
145 // If processor does not support MONITOR/MWAIT feature\r
146 // by CPUID.[EAX=01H]:ECX.BIT3, force AP in Hlt-loop mode\r
147 //\r
148 ApLoopMode = ApInHltLoop;\r
149 }\r
150 }\r
151\r
152 if (ApLoopMode == ApInHltLoop) {\r
153 *MonitorFilterSize = 0;\r
154 } else if (ApLoopMode == ApInRunLoop) {\r
155 *MonitorFilterSize = sizeof (UINT32);\r
156 } else if (ApLoopMode == ApInMwaitLoop) {\r
157 //\r
158 // CPUID.[EAX=05H]:EBX.BIT0-15: Largest monitor-line size in bytes\r
159 // CPUID.[EAX=05H].EDX: C-states supported using MWAIT\r
160 //\r
161 AsmCpuid (CPUID_MONITOR_MWAIT, NULL, &RegEbx, NULL, &RegEdx);\r
162 *MonitorFilterSize = RegEbx & 0xFFFF;\r
163 }\r
164\r
165 return ApLoopMode;\r
166}\r
167\r
168/**\r
169 Get CPU MP Data pointer from the Guided HOB.\r
170\r
171 @return Pointer to Pointer to PEI CPU MP Data\r
172**/\r
173PEI_CPU_MP_DATA *\r
174GetMpHobData (\r
175 VOID\r
176 )\r
177{\r
178 EFI_HOB_GUID_TYPE *GuidHob;\r
179 VOID *DataInHob;\r
180 PEI_CPU_MP_DATA *CpuMpData;\r
181\r
182 CpuMpData = NULL;\r
183 GuidHob = GetFirstGuidHob (&gEfiCallerIdGuid);\r
184 if (GuidHob != NULL) {\r
185 DataInHob = GET_GUID_HOB_DATA (GuidHob);\r
186 CpuMpData = (PEI_CPU_MP_DATA *)(*(UINTN *)DataInHob);\r
187 }\r
188 ASSERT (CpuMpData != NULL);\r
189 return CpuMpData;\r
190}\r
191\r
192/**\r
193 Save the volatile registers required to be restored following INIT IPI.\r
194 \r
195 @param VolatileRegisters Returns buffer saved the volatile resisters\r
196**/\r
197VOID\r
198SaveVolatileRegisters (\r
199 OUT CPU_VOLATILE_REGISTERS *VolatileRegisters\r
200 )\r
201{\r
202 UINT32 RegEdx;\r
203\r
204 VolatileRegisters->Cr0 = AsmReadCr0 ();\r
205 VolatileRegisters->Cr3 = AsmReadCr3 ();\r
206 VolatileRegisters->Cr4 = AsmReadCr4 ();\r
207\r
208 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx);\r
209 if ((RegEdx & BIT2) != 0) {\r
210 //\r
211 // If processor supports Debugging Extensions feature\r
212 // by CPUID.[EAX=01H]:EDX.BIT2\r
213 //\r
214 VolatileRegisters->Dr0 = AsmReadDr0 ();\r
215 VolatileRegisters->Dr1 = AsmReadDr1 ();\r
216 VolatileRegisters->Dr2 = AsmReadDr2 ();\r
217 VolatileRegisters->Dr3 = AsmReadDr3 ();\r
218 VolatileRegisters->Dr6 = AsmReadDr6 ();\r
219 VolatileRegisters->Dr7 = AsmReadDr7 ();\r
220 }\r
221}\r
222\r
223/**\r
224 Restore the volatile registers following INIT IPI.\r
225 \r
226 @param VolatileRegisters Pointer to volatile resisters\r
227 @param IsRestoreDr TRUE: Restore DRx if supported\r
228 FALSE: Do not restore DRx\r
229**/\r
230VOID\r
231RestoreVolatileRegisters (\r
232 IN CPU_VOLATILE_REGISTERS *VolatileRegisters,\r
233 IN BOOLEAN IsRestoreDr\r
234 )\r
235{\r
236 UINT32 RegEdx;\r
237\r
238 AsmWriteCr0 (VolatileRegisters->Cr0);\r
239 AsmWriteCr3 (VolatileRegisters->Cr3);\r
240 AsmWriteCr4 (VolatileRegisters->Cr4);\r
241\r
242 if (IsRestoreDr) {\r
243 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx);\r
244 if ((RegEdx & BIT2) != 0) {\r
245 //\r
246 // If processor supports Debugging Extensions feature\r
247 // by CPUID.[EAX=01H]:EDX.BIT2\r
248 //\r
249 AsmWriteDr0 (VolatileRegisters->Dr0);\r
250 AsmWriteDr1 (VolatileRegisters->Dr1);\r
251 AsmWriteDr2 (VolatileRegisters->Dr2);\r
252 AsmWriteDr3 (VolatileRegisters->Dr3);\r
253 AsmWriteDr6 (VolatileRegisters->Dr6);\r
254 AsmWriteDr7 (VolatileRegisters->Dr7);\r
255 }\r
256 }\r
257}\r
258\r
259/**\r
260 This function will be called from AP reset code if BSP uses WakeUpAP.\r
261\r
262 @param ExchangeInfo Pointer to the MP exchange info buffer\r
263 @param NumApsExecuting Number of current executing AP\r
264**/\r
265VOID\r
266EFIAPI\r
267ApCFunction (\r
268 IN MP_CPU_EXCHANGE_INFO *ExchangeInfo,\r
269 IN UINTN NumApsExecuting\r
270 )\r
271{\r
272 PEI_CPU_MP_DATA *PeiCpuMpData;\r
273 UINTN ProcessorNumber;\r
274 EFI_AP_PROCEDURE Procedure;\r
275 UINTN BistData;\r
276 volatile UINT32 *ApStartupSignalBuffer;\r
277\r
278 PeiCpuMpData = ExchangeInfo->PeiCpuMpData;\r
279 while (TRUE) {\r
280 if (PeiCpuMpData->InitFlag) {\r
281 ProcessorNumber = NumApsExecuting;\r
282 //\r
283 // Sync BSP's Control registers to APs\r
284 //\r
285 RestoreVolatileRegisters (&PeiCpuMpData->CpuData[0].VolatileRegisters, FALSE);\r
286 //\r
287 // This is first time AP wakeup, get BIST information from AP stack\r
288 //\r
289 BistData = *(UINTN *) (PeiCpuMpData->Buffer + ProcessorNumber * PeiCpuMpData->CpuApStackSize - sizeof (UINTN));\r
290 PeiCpuMpData->CpuData[ProcessorNumber].Health.Uint32 = (UINT32) BistData;\r
291 PeiCpuMpData->CpuData[ProcessorNumber].ApicId = GetInitialApicId ();\r
292 if (PeiCpuMpData->CpuData[ProcessorNumber].ApicId >= 0xFF) {\r
293 //\r
294 // Set x2APIC mode if there are any logical processor reporting\r
295 // an APIC ID of 255 or greater.\r
296 //\r
297 AcquireSpinLock(&PeiCpuMpData->MpLock);\r
298 PeiCpuMpData->X2ApicEnable = TRUE;\r
299 ReleaseSpinLock(&PeiCpuMpData->MpLock);\r
300 }\r
301 //\r
302 // Sync BSP's Mtrr table to all wakeup APs and load microcode on APs.\r
303 //\r
304 MtrrSetAllMtrrs (&PeiCpuMpData->MtrrTable);\r
305 MicrocodeDetect ();\r
306 PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle;\r
307 } else {\r
308 //\r
309 // Execute AP function if AP is not disabled\r
310 //\r
311 GetProcessorNumber (PeiCpuMpData, &ProcessorNumber);\r
312 if (PeiCpuMpData->ApLoopMode == ApInHltLoop) {\r
313 //\r
314 // Restore AP's volatile registers saved\r
315 //\r
316 RestoreVolatileRegisters (&PeiCpuMpData->CpuData[ProcessorNumber].VolatileRegisters, TRUE);\r
317 }\r
318\r
319 if ((PeiCpuMpData->CpuData[ProcessorNumber].State != CpuStateDisabled) &&\r
320 (PeiCpuMpData->ApFunction != 0)) {\r
321 PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateBusy;\r
322 Procedure = (EFI_AP_PROCEDURE)(UINTN)PeiCpuMpData->ApFunction;\r
323 //\r
324 // Invoke AP function here\r
325 //\r
326 Procedure ((VOID *)(UINTN)PeiCpuMpData->ApFunctionArgument);\r
327 //\r
328 // Re-get the processor number due to BSP/AP maybe exchange in AP function\r
329 //\r
330 GetProcessorNumber (PeiCpuMpData, &ProcessorNumber);\r
331 PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle;\r
332 }\r
333 }\r
334\r
335 //\r
336 // AP finished executing C code\r
337 //\r
338 InterlockedIncrement ((UINT32 *)&PeiCpuMpData->FinishedCount);\r
339\r
340 //\r
341 // Place AP is specified loop mode\r
342 //\r
343 if (PeiCpuMpData->ApLoopMode == ApInHltLoop) {\r
344 //\r
345 // Save AP volatile registers\r
346 //\r
347 SaveVolatileRegisters (&PeiCpuMpData->CpuData[ProcessorNumber].VolatileRegisters);\r
348 //\r
349 // Place AP in Hlt-loop\r
350 //\r
351 while (TRUE) {\r
352 DisableInterrupts ();\r
353 CpuSleep ();\r
354 CpuPause ();\r
355 }\r
356 }\r
357 ApStartupSignalBuffer = PeiCpuMpData->CpuData[ProcessorNumber].StartupApSignal;\r
358 while (TRUE) {\r
359 DisableInterrupts ();\r
360 if (PeiCpuMpData->ApLoopMode == ApInMwaitLoop) {\r
361 //\r
362 // Place AP in Mwait-loop\r
363 //\r
364 AsmMonitor ((UINTN)ApStartupSignalBuffer, 0, 0);\r
365 if (*ApStartupSignalBuffer != WAKEUP_AP_SIGNAL) {\r
366 //\r
367 // If AP start-up signal is not set, place AP into\r
368 // the maximum C-state\r
369 //\r
370 AsmMwait (PeiCpuMpData->ApTargetCState << 4, 0);\r
371 }\r
372 } else if (PeiCpuMpData->ApLoopMode == ApInRunLoop) {\r
373 //\r
374 // Place AP in Run-loop\r
375 //\r
376 CpuPause ();\r
377 } else {\r
378 ASSERT (FALSE);\r
379 }\r
380\r
381 //\r
382 // If AP start-up signal is written, AP is waken up\r
383 // otherwise place AP in loop again\r
384 //\r
385 if (*ApStartupSignalBuffer == WAKEUP_AP_SIGNAL) {\r
386 //\r
387 // Clear AP start-up signal when AP waken up\r
388 //\r
389 InterlockedCompareExchange32 (\r
390 (UINT32 *)ApStartupSignalBuffer,\r
391 WAKEUP_AP_SIGNAL,\r
392 0\r
393 );\r
394 break;\r
395 }\r
396 }\r
397 }\r
398}\r
399\r
400/**\r
401 Write AP start-up signal to wakeup AP.\r
402\r
403 @param ApStartupSignalBuffer Pointer to AP wakeup signal\r
404**/\r
405VOID\r
406WriteStartupSignal (\r
407 IN volatile UINT32 *ApStartupSignalBuffer\r
408 )\r
409{\r
410 *ApStartupSignalBuffer = WAKEUP_AP_SIGNAL;\r
411 //\r
412 // If AP is waken up, StartupApSignal should be cleared.\r
413 // Otherwise, write StartupApSignal again till AP waken up.\r
414 //\r
415 while (InterlockedCompareExchange32 (\r
416 (UINT32 *)ApStartupSignalBuffer,\r
417 WAKEUP_AP_SIGNAL,\r
418 WAKEUP_AP_SIGNAL\r
419 ) != 0) {\r
420 CpuPause ();\r
421 }\r
422}\r
423\r
424/**\r
425 This function will be called by BSP to wakeup AP.\r
426\r
427 @param PeiCpuMpData Pointer to PEI CPU MP Data\r
428 @param Broadcast TRUE: Send broadcast IPI to all APs\r
429 FALSE: Send IPI to AP by ApicId\r
430 @param ProcessorNumber The handle number of specified processor\r
431 @param Procedure The function to be invoked by AP\r
432 @param ProcedureArgument The argument to be passed into AP function\r
433**/\r
434VOID\r
435WakeUpAP (\r
436 IN PEI_CPU_MP_DATA *PeiCpuMpData,\r
437 IN BOOLEAN Broadcast,\r
438 IN UINTN ProcessorNumber,\r
439 IN EFI_AP_PROCEDURE Procedure, OPTIONAL\r
440 IN VOID *ProcedureArgument OPTIONAL\r
441 )\r
442{\r
443 volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;\r
444 UINTN Index;\r
445\r
446 PeiCpuMpData->ApFunction = (UINTN) Procedure;\r
447 PeiCpuMpData->ApFunctionArgument = (UINTN) ProcedureArgument;\r
448 PeiCpuMpData->FinishedCount = 0;\r
449\r
450 ExchangeInfo = PeiCpuMpData->MpCpuExchangeInfo;\r
451 ExchangeInfo->Lock = 0;\r
452 ExchangeInfo->StackStart = PeiCpuMpData->Buffer;\r
453 ExchangeInfo->StackSize = PeiCpuMpData->CpuApStackSize;\r
454 ExchangeInfo->BufferStart = PeiCpuMpData->WakeupBuffer;\r
455 ExchangeInfo->PmodeOffset = PeiCpuMpData->AddressMap.PModeEntryOffset;\r
456 ExchangeInfo->LmodeOffset = PeiCpuMpData->AddressMap.LModeEntryOffset;\r
457 ExchangeInfo->Cr3 = AsmReadCr3 ();\r
458 ExchangeInfo->CFunction = (UINTN) ApCFunction;\r
459 ExchangeInfo->NumApsExecuting = 0;\r
460 ExchangeInfo->PeiCpuMpData = PeiCpuMpData;\r
461\r
462 //\r
463 // Get the BSP's data of GDT and IDT\r
464 //\r
465 CopyMem ((VOID *)&ExchangeInfo->GdtrProfile, &mGdt, sizeof(mGdt));\r
466 AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile);\r
467\r
468 if (PeiCpuMpData->ApLoopMode == ApInMwaitLoop) {\r
469 //\r
470 // Get AP target C-state each time when waking up AP,\r
471 // for it maybe updated by platform again\r
472 //\r
473 PeiCpuMpData->ApTargetCState = PcdGet8 (PcdCpuApTargetCstate);\r
474 }\r
475\r
476 //\r
477 // Wakeup APs per AP loop state\r
478 //\r
479 if (PeiCpuMpData->ApLoopMode == ApInHltLoop || PeiCpuMpData->InitFlag) {\r
480 if (Broadcast) {\r
481 SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart);\r
482 } else {\r
483 SendInitSipiSipi (\r
484 PeiCpuMpData->CpuData[ProcessorNumber].ApicId,\r
485 (UINT32) ExchangeInfo->BufferStart\r
486 );\r
487 }\r
488 } else if ((PeiCpuMpData->ApLoopMode == ApInMwaitLoop) ||\r
489 (PeiCpuMpData->ApLoopMode == ApInRunLoop)) {\r
490 if (Broadcast) {\r
491 for (Index = 0; Index < PeiCpuMpData->CpuCount; Index++) {\r
492 if (Index != PeiCpuMpData->BspNumber) {\r
493 WriteStartupSignal (PeiCpuMpData->CpuData[Index].StartupApSignal);\r
494 }\r
495 }\r
496 } else {\r
497 WriteStartupSignal (PeiCpuMpData->CpuData[ProcessorNumber].StartupApSignal);\r
498 }\r
499 } else {\r
500 ASSERT (FALSE);\r
501 }\r
502 return ;\r
503}\r
504\r
505/**\r
506 Get available system memory below 1MB by specified size.\r
507\r
508 @param WakeupBufferSize Wakeup buffer size required\r
509\r
510 @retval other Return wakeup buffer address below 1MB.\r
511 @retval -1 Cannot find free memory below 1MB.\r
512**/\r
513UINTN\r
514GetWakeupBuffer (\r
515 IN UINTN WakeupBufferSize\r
516 )\r
517{\r
518 EFI_PEI_HOB_POINTERS Hob;\r
519 UINTN WakeupBufferStart;\r
520 UINTN WakeupBufferEnd;\r
521\r
522 //\r
523 // Get the HOB list for processing\r
524 //\r
525 Hob.Raw = GetHobList ();\r
526\r
527 //\r
528 // Collect memory ranges\r
529 //\r
530 while (!END_OF_HOB_LIST (Hob)) {\r
531 if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {\r
532 if ((Hob.ResourceDescriptor->PhysicalStart < BASE_1MB) &&\r
533 (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) &&\r
534 ((Hob.ResourceDescriptor->ResourceAttribute &\r
535 (EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED |\r
536 EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED |\r
537 EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED\r
538 )) == 0)\r
539 ) {\r
540 //\r
541 // Need memory under 1MB to be collected here\r
542 //\r
543 WakeupBufferEnd = (UINTN) (Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength);\r
544 if (WakeupBufferEnd > BASE_1MB) {\r
545 //\r
546 // Wakeup buffer should be under 1MB\r
547 //\r
548 WakeupBufferEnd = BASE_1MB;\r
549 }\r
550 //\r
551 // Wakeup buffer should be aligned on 4KB\r
552 //\r
553 WakeupBufferStart = (WakeupBufferEnd - WakeupBufferSize) & ~(SIZE_4KB - 1);\r
554 if (WakeupBufferStart < Hob.ResourceDescriptor->PhysicalStart) {\r
555 continue;\r
556 }\r
557 //\r
558 // Create a memory allocation HOB.\r
559 //\r
560 BuildMemoryAllocationHob (\r
561 WakeupBufferStart,\r
562 WakeupBufferSize,\r
563 EfiBootServicesData\r
564 );\r
565 return WakeupBufferStart;\r
566 }\r
567 }\r
568 //\r
569 // Find the next HOB\r
570 //\r
571 Hob.Raw = GET_NEXT_HOB (Hob);\r
572 }\r
573\r
574 return (UINTN) -1;\r
575}\r
576\r
577/**\r
578 Get available system memory below 1MB by specified size.\r
579\r
580 @param PeiCpuMpData Pointer to PEI CPU MP Data\r
581**/\r
582VOID\r
583BackupAndPrepareWakeupBuffer(\r
584 IN PEI_CPU_MP_DATA *PeiCpuMpData\r
585 )\r
586{\r
587 CopyMem (\r
588 (VOID *) PeiCpuMpData->BackupBuffer,\r
589 (VOID *) PeiCpuMpData->WakeupBuffer,\r
590 PeiCpuMpData->BackupBufferSize\r
591 );\r
592 CopyMem (\r
593 (VOID *) PeiCpuMpData->WakeupBuffer,\r
594 (VOID *) PeiCpuMpData->AddressMap.RendezvousFunnelAddress,\r
595 PeiCpuMpData->AddressMap.RendezvousFunnelSize\r
596 );\r
597}\r
598\r
599/**\r
600 Restore wakeup buffer data.\r
601\r
602 @param PeiCpuMpData Pointer to PEI CPU MP Data\r
603**/\r
604VOID\r
605RestoreWakeupBuffer(\r
606 IN PEI_CPU_MP_DATA *PeiCpuMpData\r
607 )\r
608{\r
609 CopyMem ((VOID *) PeiCpuMpData->WakeupBuffer, (VOID *) PeiCpuMpData->BackupBuffer, PeiCpuMpData->BackupBufferSize);\r
610}\r
611\r
612/**\r
613 This function will get CPU count in the system.\r
614\r
615 @param PeiCpuMpData Pointer to PEI CPU MP Data\r
616\r
617 @return AP processor count\r
618**/\r
619UINT32\r
620CountProcessorNumber (\r
621 IN PEI_CPU_MP_DATA *PeiCpuMpData\r
622 )\r
623{\r
624 //\r
625 // Load Microcode on BSP\r
626 //\r
627 MicrocodeDetect ();\r
628 //\r
629 // Store BSP's MTRR setting\r
630 //\r
631 MtrrGetAllMtrrs (&PeiCpuMpData->MtrrTable);\r
632\r
633 //\r
634 // Only perform AP detection if PcdCpuMaxLogicalProcessorNumber is greater than 1\r
635 //\r
636 if (PcdGet32 (PcdCpuMaxLogicalProcessorNumber) > 1) {\r
637 //\r
638 // Send 1st broadcast IPI to APs to wakeup APs\r
639 //\r
640 PeiCpuMpData->InitFlag = TRUE;\r
641 PeiCpuMpData->X2ApicEnable = FALSE;\r
642 WakeUpAP (PeiCpuMpData, TRUE, 0, NULL, NULL);\r
643 //\r
644 // Wait for AP task to complete and then exit.\r
645 //\r
646 MicroSecondDelay (PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds));\r
647 PeiCpuMpData->InitFlag = FALSE;\r
648 PeiCpuMpData->CpuCount += (UINT32)PeiCpuMpData->MpCpuExchangeInfo->NumApsExecuting;\r
649 ASSERT (PeiCpuMpData->CpuCount <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber));\r
650 //\r
651 // Wait for all APs finished the initialization\r
652 //\r
653 while (PeiCpuMpData->FinishedCount < (PeiCpuMpData->CpuCount - 1)) {\r
654 CpuPause ();\r
655 }\r
656\r
657 if (PeiCpuMpData->X2ApicEnable) {\r
658 DEBUG ((EFI_D_INFO, "Force x2APIC mode!\n"));\r
659 //\r
660 // Wakeup all APs to enable x2APIC mode\r
661 //\r
662 WakeUpAP (PeiCpuMpData, TRUE, 0, ApFuncEnableX2Apic, NULL);\r
663 //\r
664 // Wait for all known APs finished\r
665 //\r
666 while (PeiCpuMpData->FinishedCount < (PeiCpuMpData->CpuCount - 1)) {\r
667 CpuPause ();\r
668 }\r
669 //\r
670 // Enable x2APIC on BSP\r
671 //\r
672 SetApicMode (LOCAL_APIC_MODE_X2APIC);\r
673 }\r
674 DEBUG ((EFI_D_INFO, "APIC MODE is %d\n", GetApicMode ()));\r
675 //\r
676 // Sort BSP/Aps by CPU APIC ID in ascending order\r
677 //\r
678 SortApicId (PeiCpuMpData);\r
679 }\r
680\r
681 DEBUG ((EFI_D_INFO, "CpuMpPei: Find %d processors in system.\n", PeiCpuMpData->CpuCount));\r
682 return PeiCpuMpData->CpuCount;\r
683}\r
684\r
685/**\r
686 Prepare for AP wakeup buffer and copy AP reset code into it.\r
687\r
688 Get wakeup buffer below 1MB. Allocate memory for CPU MP Data and APs Stack.\r
689\r
690 @return Pointer to PEI CPU MP Data\r
691**/\r
692PEI_CPU_MP_DATA *\r
693PrepareAPStartupVector (\r
694 VOID\r
695 )\r
696{\r
697 EFI_STATUS Status;\r
698 UINT32 MaxCpuCount;\r
699 PEI_CPU_MP_DATA *PeiCpuMpData;\r
700 EFI_PHYSICAL_ADDRESS Buffer;\r
701 UINTN BufferSize;\r
702 UINTN WakeupBuffer;\r
703 UINTN WakeupBufferSize;\r
704 MP_ASSEMBLY_ADDRESS_MAP AddressMap;\r
705 UINT8 ApLoopMode;\r
706 UINT16 MonitorFilterSize;\r
707 UINT8 *MonitorBuffer;\r
708 UINTN Index;\r
709\r
710 AsmGetAddressMap (&AddressMap);\r
711 WakeupBufferSize = AddressMap.RendezvousFunnelSize + sizeof (MP_CPU_EXCHANGE_INFO);\r
712 WakeupBuffer = GetWakeupBuffer ((WakeupBufferSize + SIZE_4KB - 1) & ~(SIZE_4KB - 1));\r
713 ASSERT (WakeupBuffer != (UINTN) -1);\r
714 DEBUG ((EFI_D_INFO, "CpuMpPei: WakeupBuffer = 0x%x\n", WakeupBuffer));\r
715\r
716 //\r
717 // Allocate Pages for APs stack, CPU MP Data, backup buffer for wakeup buffer,\r
718 // and monitor buffer if required.\r
719 //\r
720 MaxCpuCount = PcdGet32(PcdCpuMaxLogicalProcessorNumber);\r
721 BufferSize = PcdGet32 (PcdCpuApStackSize) * MaxCpuCount + sizeof (PEI_CPU_MP_DATA)\r
722 + WakeupBufferSize + sizeof (PEI_CPU_DATA) * MaxCpuCount;\r
723 ApLoopMode = GetApLoopMode (&MonitorFilterSize);\r
724 BufferSize += MonitorFilterSize * MaxCpuCount;\r
725 Status = PeiServicesAllocatePages (\r
726 EfiBootServicesData,\r
727 EFI_SIZE_TO_PAGES (BufferSize),\r
728 &Buffer\r
729 );\r
730 ASSERT_EFI_ERROR (Status);\r
731\r
732 PeiCpuMpData = (PEI_CPU_MP_DATA *) (UINTN) (Buffer + PcdGet32 (PcdCpuApStackSize) * MaxCpuCount);\r
733 PeiCpuMpData->Buffer = (UINTN) Buffer;\r
734 PeiCpuMpData->CpuApStackSize = PcdGet32 (PcdCpuApStackSize);\r
735 PeiCpuMpData->WakeupBuffer = WakeupBuffer;\r
736 PeiCpuMpData->BackupBuffer = (UINTN)PeiCpuMpData + sizeof (PEI_CPU_MP_DATA);\r
737 PeiCpuMpData->BackupBufferSize = WakeupBufferSize;\r
738 PeiCpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) (WakeupBuffer + AddressMap.RendezvousFunnelSize);\r
739\r
740 PeiCpuMpData->CpuCount = 1;\r
741 PeiCpuMpData->BspNumber = 0;\r
742 PeiCpuMpData->CpuData = (PEI_CPU_DATA *) (PeiCpuMpData->BackupBuffer +\r
743 PeiCpuMpData->BackupBufferSize);\r
744 PeiCpuMpData->CpuData[0].ApicId = GetInitialApicId ();\r
745 PeiCpuMpData->CpuData[0].Health.Uint32 = 0;\r
746 PeiCpuMpData->EndOfPeiFlag = FALSE;\r
747 InitializeSpinLock(&PeiCpuMpData->MpLock);\r
748 SaveVolatileRegisters (&PeiCpuMpData->CpuData[0].VolatileRegisters);\r
749 CopyMem (&PeiCpuMpData->AddressMap, &AddressMap, sizeof (MP_ASSEMBLY_ADDRESS_MAP));\r
750 //\r
751 // Initialize AP loop mode\r
752 //\r
753 PeiCpuMpData->ApLoopMode = ApLoopMode;\r
754 DEBUG ((EFI_D_INFO, "AP Loop Mode is %d\n", PeiCpuMpData->ApLoopMode));\r
755 MonitorBuffer = (UINT8 *)(PeiCpuMpData->CpuData + MaxCpuCount);\r
756 if (PeiCpuMpData->ApLoopMode != ApInHltLoop) {\r
757 //\r
758 // Set up APs wakeup signal buffer\r
759 //\r
760 for (Index = 0; Index < MaxCpuCount; Index++) {\r
761 PeiCpuMpData->CpuData[Index].StartupApSignal = \r
762 (UINT32 *)(MonitorBuffer + MonitorFilterSize * Index);\r
763 }\r
764 }\r
765 //\r
766 // Backup original data and copy AP reset code in it\r
767 //\r
768 BackupAndPrepareWakeupBuffer(PeiCpuMpData);\r
769\r
770 return PeiCpuMpData;\r
771}\r
772\r
773/**\r
774 Notify function on End Of Pei PPI.\r
775\r
776 On S3 boot, this function will restore wakeup buffer data.\r
777 On normal boot, this function will flag wakeup buffer to be un-used type.\r
778\r
779 @param PeiServices The pointer to the PEI Services Table.\r
780 @param NotifyDescriptor Address of the notification descriptor data structure.\r
781 @param Ppi Address of the PPI that was installed.\r
782\r
783 @retval EFI_SUCCESS When everything is OK.\r
784\r
785**/\r
786EFI_STATUS\r
787EFIAPI\r
788CpuMpEndOfPeiCallback (\r
789 IN EFI_PEI_SERVICES **PeiServices,\r
790 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,\r
791 IN VOID *Ppi\r
792 )\r
793{\r
794 EFI_STATUS Status;\r
795 EFI_BOOT_MODE BootMode;\r
796 PEI_CPU_MP_DATA *PeiCpuMpData;\r
797 EFI_PEI_HOB_POINTERS Hob;\r
798 EFI_HOB_MEMORY_ALLOCATION *MemoryHob;\r
799\r
800 DEBUG ((EFI_D_INFO, "CpuMpPei: CpuMpEndOfPeiCallback () invoked\n"));\r
801\r
802 Status = PeiServicesGetBootMode (&BootMode);\r
803 ASSERT_EFI_ERROR (Status);\r
804\r
805 PeiCpuMpData = GetMpHobData ();\r
806 ASSERT (PeiCpuMpData != NULL);\r
807\r
808 if (BootMode != BOOT_ON_S3_RESUME) {\r
809 //\r
810 // Get the HOB list for processing\r
811 //\r
812 Hob.Raw = GetHobList ();\r
813 //\r
814 // Collect memory ranges\r
815 //\r
816 while (!END_OF_HOB_LIST (Hob)) {\r
817 if (Hob.Header->HobType == EFI_HOB_TYPE_MEMORY_ALLOCATION) {\r
818 MemoryHob = Hob.MemoryAllocation;\r
819 if(MemoryHob->AllocDescriptor.MemoryBaseAddress == PeiCpuMpData->WakeupBuffer) {\r
820 //\r
821 // Flag this HOB type to un-used\r
822 //\r
823 GET_HOB_TYPE (Hob) = EFI_HOB_TYPE_UNUSED;\r
824 break;\r
825 }\r
826 }\r
827 Hob.Raw = GET_NEXT_HOB (Hob);\r
828 }\r
829 } else {\r
830 RestoreWakeupBuffer (PeiCpuMpData);\r
831 PeiCpuMpData->EndOfPeiFlag = TRUE;\r
832 }\r
833 return EFI_SUCCESS;\r
834}\r
835\r
836/**\r
837 The Entry point of the MP CPU PEIM.\r
838\r
839 This function will wakeup APs and collect CPU AP count and install the\r
840 Mp Service Ppi.\r
841\r
842 @param FileHandle Handle of the file being invoked.\r
843 @param PeiServices Describes the list of possible PEI Services.\r
844\r
845 @retval EFI_SUCCESS MpServicePpi is installed successfully.\r
846\r
847**/\r
848EFI_STATUS\r
849EFIAPI\r
850CpuMpPeimInit (\r
851 IN EFI_PEI_FILE_HANDLE FileHandle,\r
852 IN CONST EFI_PEI_SERVICES **PeiServices\r
853 )\r
854{\r
855 EFI_STATUS Status;\r
856 PEI_CPU_MP_DATA *PeiCpuMpData;\r
857\r
858 //\r
859 // Load new GDT table on BSP\r
860 //\r
861 AsmInitializeGdt (&mGdt);\r
862 //\r
863 // Get wakeup buffer and copy AP reset code in it\r
864 //\r
865 PeiCpuMpData = PrepareAPStartupVector ();\r
866 //\r
867 // Count processor number and collect processor information\r
868 //\r
869 CountProcessorNumber (PeiCpuMpData);\r
870 //\r
871 // Build location of PEI CPU MP DATA buffer in HOB\r
872 //\r
873 BuildGuidDataHob (\r
874 &gEfiCallerIdGuid,\r
875 (VOID *)&PeiCpuMpData,\r
876 sizeof(UINT64)\r
877 );\r
878 //\r
879 // Update and publish CPU BIST information\r
880 //\r
881 CollectBistDataFromPpi (PeiServices, PeiCpuMpData);\r
882 //\r
883 // register an event for EndOfPei\r
884 //\r
885 Status = PeiServicesNotifyPpi (&mNotifyList);\r
886 ASSERT_EFI_ERROR (Status);\r
887 //\r
888 // Install CPU MP PPI\r
889 //\r
890 Status = PeiServicesInstallPpi(&mPeiCpuMpPpiDesc);\r
891 ASSERT_EFI_ERROR (Status);\r
892\r
893 return Status;\r
894}\r