]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/CpuMpPei/CpuMpPei.c
UefiCpuPkg/CpuMpPei: Add GetApLoopMode() to get AP loop mode
[mirror_edk2.git] / UefiCpuPkg / CpuMpPei / CpuMpPei.c
CommitLineData
ea0f431c
JF
1/** @file\r
2 CPU PEI Module installs CPU Multiple Processor PPI.\r
3\r
4 Copyright (c) 2015, 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
24930b56 64 PEI_CPU_DATA CpuData;\r
ea0f431c
JF
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
24930b56
JF
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
ea0f431c
JF
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
c7981a11
JF
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
4de216c0
JF
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
ea0f431c
JF
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
ef1fdb80 192/**\r
5ac96e3a 193 Save the volatile registers required to be restored following INIT IPI.\r
ef1fdb80
JF
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
5ac96e3a 224 Restore the volatile registers following INIT IPI.\r
ef1fdb80
JF
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
ea0f431c
JF
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
c972495e 263 @param NumApsExecuting Number of current executing AP\r
ea0f431c
JF
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\r
277 PeiCpuMpData = ExchangeInfo->PeiCpuMpData;\r
278 if (PeiCpuMpData->InitFlag) {\r
10c6c206 279 ProcessorNumber = NumApsExecuting;\r
ea0f431c 280 //\r
22cfe73a
JF
281 // Sync BSP's Control registers to APs\r
282 //\r
283 RestoreVolatileRegisters (&PeiCpuMpData->CpuData[0].VolatileRegisters, FALSE);\r
284 //\r
2f0261b7 285 // This is first time AP wakeup, get BIST information from AP stack\r
ea0f431c 286 //\r
10c6c206
JF
287 BistData = *(UINTN *) (PeiCpuMpData->Buffer + ProcessorNumber * PeiCpuMpData->CpuApStackSize - sizeof (UINTN));\r
288 PeiCpuMpData->CpuData[ProcessorNumber].Health.Uint32 = (UINT32) BistData;\r
289 PeiCpuMpData->CpuData[ProcessorNumber].ApicId = GetInitialApicId ();\r
290 if (PeiCpuMpData->CpuData[ProcessorNumber].ApicId >= 0xFF) {\r
2f0261b7
JF
291 //\r
292 // Set x2APIC mode if there are any logical processor reporting\r
293 // an APIC ID of 255 or greater.\r
294 //\r
295 AcquireSpinLock(&PeiCpuMpData->MpLock);\r
296 PeiCpuMpData->X2ApicEnable = TRUE;\r
297 ReleaseSpinLock(&PeiCpuMpData->MpLock);\r
298 }\r
ea0f431c
JF
299 //\r
300 // Sync BSP's Mtrr table to all wakeup APs and load microcode on APs.\r
301 //\r
302 MtrrSetAllMtrrs (&PeiCpuMpData->MtrrTable);\r
303 MicrocodeDetect ();\r
10c6c206 304 PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle;\r
ea0f431c
JF
305 } else {\r
306 //\r
307 // Execute AP function if AP is not disabled\r
308 //\r
309 GetProcessorNumber (PeiCpuMpData, &ProcessorNumber);\r
f40a7de4
JF
310 //\r
311 // Restore AP's volatile registers saved\r
312 //\r
313 RestoreVolatileRegisters (&PeiCpuMpData->CpuData[ProcessorNumber].VolatileRegisters, TRUE);\r
314\r
ea0f431c
JF
315 if ((PeiCpuMpData->CpuData[ProcessorNumber].State != CpuStateDisabled) &&\r
316 (PeiCpuMpData->ApFunction != 0)) {\r
317 PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateBusy;\r
318 Procedure = (EFI_AP_PROCEDURE)(UINTN)PeiCpuMpData->ApFunction;\r
c972495e
JF
319 //\r
320 // Invoke AP function here\r
321 //\r
ea0f431c
JF
322 Procedure ((VOID *)(UINTN)PeiCpuMpData->ApFunctionArgument);\r
323 PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle;\r
324 }\r
325 }\r
326\r
327 //\r
328 // AP finished executing C code\r
329 //\r
330 InterlockedIncrement ((UINT32 *)&PeiCpuMpData->FinishedCount);\r
331\r
f40a7de4
JF
332 //\r
333 // Save AP volatile registers\r
334 //\r
335 SaveVolatileRegisters (&PeiCpuMpData->CpuData[ProcessorNumber].VolatileRegisters);\r
336\r
ea0f431c
JF
337 AsmCliHltLoop ();\r
338}\r
339\r
340/**\r
341 This function will be called by BSP to wakeup AP.\r
342\r
343 @param PeiCpuMpData Pointer to PEI CPU MP Data\r
344 @param Broadcast TRUE: Send broadcast IPI to all APs\r
345 FALSE: Send IPI to AP by ApicId\r
346 @param ApicId Apic ID for the processor to be waked\r
347 @param Procedure The function to be invoked by AP\r
348 @param ProcedureArgument The argument to be passed into AP function\r
349**/\r
350VOID\r
351WakeUpAP (\r
352 IN PEI_CPU_MP_DATA *PeiCpuMpData,\r
353 IN BOOLEAN Broadcast,\r
354 IN UINT32 ApicId,\r
355 IN EFI_AP_PROCEDURE Procedure, OPTIONAL\r
356 IN VOID *ProcedureArgument OPTIONAL\r
357 )\r
358{\r
359 volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;\r
360\r
361 PeiCpuMpData->ApFunction = (UINTN) Procedure;\r
362 PeiCpuMpData->ApFunctionArgument = (UINTN) ProcedureArgument;\r
363 PeiCpuMpData->FinishedCount = 0;\r
364\r
365 ExchangeInfo = PeiCpuMpData->MpCpuExchangeInfo;\r
366 ExchangeInfo->Lock = 0;\r
367 ExchangeInfo->StackStart = PeiCpuMpData->Buffer;\r
368 ExchangeInfo->StackSize = PeiCpuMpData->CpuApStackSize;\r
369 ExchangeInfo->BufferStart = PeiCpuMpData->WakeupBuffer;\r
370 ExchangeInfo->PmodeOffset = PeiCpuMpData->AddressMap.PModeEntryOffset;\r
371 ExchangeInfo->LmodeOffset = PeiCpuMpData->AddressMap.LModeEntryOffset;\r
372 ExchangeInfo->Cr3 = AsmReadCr3 ();\r
373 ExchangeInfo->CFunction = (UINTN) ApCFunction;\r
374 ExchangeInfo->NumApsExecuting = 0;\r
375 ExchangeInfo->PeiCpuMpData = PeiCpuMpData;\r
376\r
377 //\r
378 // Get the BSP's data of GDT and IDT\r
379 //\r
380 CopyMem ((VOID *)&ExchangeInfo->GdtrProfile, &mGdt, sizeof(mGdt));\r
381 AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile);\r
382\r
383 if (Broadcast) {\r
384 SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart);\r
385 } else {\r
386 SendInitSipiSipi (ApicId, (UINT32) ExchangeInfo->BufferStart);\r
387 }\r
388\r
389 return ;\r
390}\r
391\r
392/**\r
393 Get available system memory below 1MB by specified size.\r
394\r
395 @param WakeupBufferSize Wakeup buffer size required\r
396\r
397 @retval other Return wakeup buffer address below 1MB.\r
398 @retval -1 Cannot find free memory below 1MB.\r
399**/\r
400UINTN\r
401GetWakeupBuffer (\r
402 IN UINTN WakeupBufferSize\r
403 )\r
404{\r
405 EFI_PEI_HOB_POINTERS Hob;\r
406 UINTN WakeupBufferStart;\r
407 UINTN WakeupBufferEnd;\r
408\r
409 //\r
410 // Get the HOB list for processing\r
411 //\r
412 Hob.Raw = GetHobList ();\r
413\r
414 //\r
415 // Collect memory ranges\r
416 //\r
417 while (!END_OF_HOB_LIST (Hob)) {\r
418 if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {\r
419 if ((Hob.ResourceDescriptor->PhysicalStart < BASE_1MB) &&\r
420 (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) &&\r
421 ((Hob.ResourceDescriptor->ResourceAttribute &\r
422 (EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED |\r
423 EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED |\r
424 EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED\r
425 )) == 0)\r
426 ) {\r
427 //\r
428 // Need memory under 1MB to be collected here\r
429 //\r
430 WakeupBufferEnd = (UINTN) (Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength);\r
431 if (WakeupBufferEnd > BASE_1MB) {\r
432 //\r
433 // Wakeup buffer should be under 1MB\r
434 //\r
435 WakeupBufferEnd = BASE_1MB;\r
436 }\r
437 //\r
438 // Wakeup buffer should be aligned on 4KB\r
439 //\r
440 WakeupBufferStart = (WakeupBufferEnd - WakeupBufferSize) & ~(SIZE_4KB - 1);\r
441 if (WakeupBufferStart < Hob.ResourceDescriptor->PhysicalStart) {\r
442 continue;\r
443 }\r
444 //\r
445 // Create a memory allocation HOB.\r
446 //\r
447 BuildMemoryAllocationHob (\r
448 WakeupBufferStart,\r
449 WakeupBufferSize,\r
450 EfiBootServicesData\r
451 );\r
452 return WakeupBufferStart;\r
453 }\r
454 }\r
455 //\r
456 // Find the next HOB\r
457 //\r
458 Hob.Raw = GET_NEXT_HOB (Hob);\r
459 }\r
460\r
461 return (UINTN) -1;\r
462}\r
463\r
464/**\r
465 Get available system memory below 1MB by specified size.\r
466\r
467 @param PeiCpuMpData Pointer to PEI CPU MP Data\r
468**/\r
469VOID\r
470BackupAndPrepareWakeupBuffer(\r
471 IN PEI_CPU_MP_DATA *PeiCpuMpData\r
472 )\r
473{\r
474 CopyMem (\r
475 (VOID *) PeiCpuMpData->BackupBuffer,\r
476 (VOID *) PeiCpuMpData->WakeupBuffer,\r
477 PeiCpuMpData->BackupBufferSize\r
478 );\r
479 CopyMem (\r
480 (VOID *) PeiCpuMpData->WakeupBuffer,\r
481 (VOID *) PeiCpuMpData->AddressMap.RendezvousFunnelAddress,\r
482 PeiCpuMpData->AddressMap.RendezvousFunnelSize\r
483 );\r
484}\r
485\r
486/**\r
487 Restore wakeup buffer data.\r
488\r
489 @param PeiCpuMpData Pointer to PEI CPU MP Data\r
490**/\r
491VOID\r
492RestoreWakeupBuffer(\r
493 IN PEI_CPU_MP_DATA *PeiCpuMpData\r
494 )\r
495{\r
496 CopyMem ((VOID *) PeiCpuMpData->WakeupBuffer, (VOID *) PeiCpuMpData->BackupBuffer, PeiCpuMpData->BackupBufferSize);\r
497}\r
498\r
499/**\r
500 This function will get CPU count in the system.\r
501\r
502 @param PeiCpuMpData Pointer to PEI CPU MP Data\r
503\r
504 @return AP processor count\r
505**/\r
506UINT32\r
507CountProcessorNumber (\r
508 IN PEI_CPU_MP_DATA *PeiCpuMpData\r
509 )\r
510{\r
511 //\r
512 // Load Microcode on BSP\r
513 //\r
514 MicrocodeDetect ();\r
515 //\r
516 // Store BSP's MTRR setting\r
517 //\r
518 MtrrGetAllMtrrs (&PeiCpuMpData->MtrrTable);\r
944f45ae 519\r
ea0f431c 520 //\r
944f45ae 521 // Only perform AP detection if PcdCpuMaxLogicalProcessorNumber is greater than 1\r
ea0f431c 522 //\r
944f45ae
MK
523 if (PcdGet32 (PcdCpuMaxLogicalProcessorNumber) > 1) {\r
524 //\r
2f0261b7 525 // Send 1st broadcast IPI to APs to wakeup APs\r
944f45ae 526 //\r
2f0261b7
JF
527 PeiCpuMpData->InitFlag = TRUE;\r
528 PeiCpuMpData->X2ApicEnable = FALSE;\r
944f45ae
MK
529 WakeUpAP (PeiCpuMpData, TRUE, 0, NULL, NULL);\r
530 //\r
531 // Wait for AP task to complete and then exit.\r
532 //\r
533 MicroSecondDelay (PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds));\r
2f0261b7 534 PeiCpuMpData->InitFlag = FALSE;\r
944f45ae
MK
535 PeiCpuMpData->CpuCount += (UINT32)PeiCpuMpData->MpCpuExchangeInfo->NumApsExecuting;\r
536 ASSERT (PeiCpuMpData->CpuCount <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber));\r
537 //\r
c7981a11
JF
538 // Wait for all APs finished the initialization\r
539 //\r
540 while (PeiCpuMpData->FinishedCount < (PeiCpuMpData->CpuCount - 1)) {\r
541 CpuPause ();\r
542 }\r
543\r
544 if (PeiCpuMpData->X2ApicEnable) {\r
545 DEBUG ((EFI_D_INFO, "Force x2APIC mode!\n"));\r
546 //\r
547 // Send 2nd broadcast IPI to all APs to enable x2APIC mode\r
548 //\r
549 WakeUpAP (PeiCpuMpData, TRUE, 0, ApFuncEnableX2Apic, NULL);\r
550 //\r
551 // Wait for all known APs finished\r
552 //\r
553 while (PeiCpuMpData->FinishedCount < (PeiCpuMpData->CpuCount - 1)) {\r
554 CpuPause ();\r
555 }\r
556 //\r
557 // Enable x2APIC on BSP\r
558 //\r
559 SetApicMode (LOCAL_APIC_MODE_X2APIC);\r
560 }\r
561 DEBUG ((EFI_D_INFO, "APIC MODE is %d\n", GetApicMode ()));\r
562 //\r
944f45ae
MK
563 // Sort BSP/Aps by CPU APIC ID in ascending order\r
564 //\r
565 SortApicId (PeiCpuMpData);\r
566 }\r
ea0f431c
JF
567\r
568 DEBUG ((EFI_D_INFO, "CpuMpPei: Find %d processors in system.\n", PeiCpuMpData->CpuCount));\r
569 return PeiCpuMpData->CpuCount;\r
570}\r
571\r
572/**\r
573 Prepare for AP wakeup buffer and copy AP reset code into it.\r
574\r
575 Get wakeup buffer below 1MB. Allocate memory for CPU MP Data and APs Stack.\r
576\r
577 @return Pointer to PEI CPU MP Data\r
578**/\r
579PEI_CPU_MP_DATA *\r
580PrepareAPStartupVector (\r
581 VOID\r
582 )\r
583{\r
584 EFI_STATUS Status;\r
585 UINT32 MaxCpuCount;\r
586 PEI_CPU_MP_DATA *PeiCpuMpData;\r
587 EFI_PHYSICAL_ADDRESS Buffer;\r
588 UINTN BufferSize;\r
589 UINTN WakeupBuffer;\r
590 UINTN WakeupBufferSize;\r
591 MP_ASSEMBLY_ADDRESS_MAP AddressMap;\r
592\r
593 AsmGetAddressMap (&AddressMap);\r
594 WakeupBufferSize = AddressMap.RendezvousFunnelSize + sizeof (MP_CPU_EXCHANGE_INFO);\r
595 WakeupBuffer = GetWakeupBuffer ((WakeupBufferSize + SIZE_4KB - 1) & ~(SIZE_4KB - 1));\r
596 ASSERT (WakeupBuffer != (UINTN) -1);\r
597 DEBUG ((EFI_D_INFO, "CpuMpPei: WakeupBuffer = 0x%x\n", WakeupBuffer));\r
598\r
599 //\r
600 // Allocate Pages for APs stack, CPU MP Data and backup buffer for wakeup buffer\r
601 //\r
602 MaxCpuCount = PcdGet32(PcdCpuMaxLogicalProcessorNumber);\r
603 BufferSize = PcdGet32 (PcdCpuApStackSize) * MaxCpuCount + sizeof (PEI_CPU_MP_DATA)\r
604 + WakeupBufferSize + sizeof (PEI_CPU_DATA) * MaxCpuCount;\r
605 Status = PeiServicesAllocatePages (\r
606 EfiBootServicesData,\r
607 EFI_SIZE_TO_PAGES (BufferSize),\r
608 &Buffer\r
609 );\r
610 ASSERT_EFI_ERROR (Status);\r
611\r
612 PeiCpuMpData = (PEI_CPU_MP_DATA *) (UINTN) (Buffer + PcdGet32 (PcdCpuApStackSize) * MaxCpuCount);\r
613 PeiCpuMpData->Buffer = (UINTN) Buffer;\r
614 PeiCpuMpData->CpuApStackSize = PcdGet32 (PcdCpuApStackSize);\r
615 PeiCpuMpData->WakeupBuffer = WakeupBuffer;\r
616 PeiCpuMpData->BackupBuffer = (UINTN)PeiCpuMpData + sizeof (PEI_CPU_MP_DATA);\r
617 PeiCpuMpData->BackupBufferSize = WakeupBufferSize;\r
618 PeiCpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) (WakeupBuffer + AddressMap.RendezvousFunnelSize);\r
619\r
620 PeiCpuMpData->CpuCount = 1;\r
621 PeiCpuMpData->BspNumber = 0;\r
28f27af6
JF
622 PeiCpuMpData->CpuData = (PEI_CPU_DATA *) (PeiCpuMpData->BackupBuffer +\r
623 PeiCpuMpData->BackupBufferSize);\r
ea0f431c
JF
624 PeiCpuMpData->CpuData[0].ApicId = GetInitialApicId ();\r
625 PeiCpuMpData->CpuData[0].Health.Uint32 = 0;\r
626 PeiCpuMpData->EndOfPeiFlag = FALSE;\r
2f0261b7 627 InitializeSpinLock(&PeiCpuMpData->MpLock);\r
22cfe73a 628 SaveVolatileRegisters (&PeiCpuMpData->CpuData[0].VolatileRegisters);\r
ea0f431c
JF
629 CopyMem (&PeiCpuMpData->AddressMap, &AddressMap, sizeof (MP_ASSEMBLY_ADDRESS_MAP));\r
630\r
631 //\r
632 // Backup original data and copy AP reset code in it\r
633 //\r
634 BackupAndPrepareWakeupBuffer(PeiCpuMpData);\r
635\r
636 return PeiCpuMpData;\r
637}\r
638\r
639/**\r
640 Notify function on End Of Pei PPI.\r
641\r
642 On S3 boot, this function will restore wakeup buffer data.\r
643 On normal boot, this function will flag wakeup buffer to be un-used type.\r
644\r
645 @param PeiServices The pointer to the PEI Services Table.\r
646 @param NotifyDescriptor Address of the notification descriptor data structure.\r
647 @param Ppi Address of the PPI that was installed.\r
648\r
649 @retval EFI_SUCCESS When everything is OK.\r
650\r
651**/\r
652EFI_STATUS\r
653EFIAPI\r
654CpuMpEndOfPeiCallback (\r
655 IN EFI_PEI_SERVICES **PeiServices,\r
656 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,\r
657 IN VOID *Ppi\r
658 )\r
659{\r
660 EFI_STATUS Status;\r
661 EFI_BOOT_MODE BootMode;\r
662 PEI_CPU_MP_DATA *PeiCpuMpData;\r
663 EFI_PEI_HOB_POINTERS Hob;\r
664 EFI_HOB_MEMORY_ALLOCATION *MemoryHob;\r
665\r
c972495e 666 DEBUG ((EFI_D_INFO, "CpuMpPei: CpuMpEndOfPeiCallback () invoked\n"));\r
ea0f431c
JF
667\r
668 Status = PeiServicesGetBootMode (&BootMode);\r
669 ASSERT_EFI_ERROR (Status);\r
670\r
671 PeiCpuMpData = GetMpHobData ();\r
672 ASSERT (PeiCpuMpData != NULL);\r
673\r
674 if (BootMode != BOOT_ON_S3_RESUME) {\r
675 //\r
676 // Get the HOB list for processing\r
677 //\r
678 Hob.Raw = GetHobList ();\r
679 //\r
680 // Collect memory ranges\r
681 //\r
682 while (!END_OF_HOB_LIST (Hob)) {\r
683 if (Hob.Header->HobType == EFI_HOB_TYPE_MEMORY_ALLOCATION) {\r
684 MemoryHob = Hob.MemoryAllocation;\r
685 if(MemoryHob->AllocDescriptor.MemoryBaseAddress == PeiCpuMpData->WakeupBuffer) {\r
686 //\r
687 // Flag this HOB type to un-used\r
688 //\r
689 GET_HOB_TYPE (Hob) = EFI_HOB_TYPE_UNUSED;\r
690 break;\r
691 }\r
692 }\r
693 Hob.Raw = GET_NEXT_HOB (Hob);\r
694 }\r
695 } else {\r
696 RestoreWakeupBuffer (PeiCpuMpData);\r
697 PeiCpuMpData->EndOfPeiFlag = TRUE;\r
698 }\r
699 return EFI_SUCCESS;\r
700}\r
701\r
702/**\r
703 The Entry point of the MP CPU PEIM.\r
704\r
705 This function will wakeup APs and collect CPU AP count and install the\r
706 Mp Service Ppi.\r
707\r
708 @param FileHandle Handle of the file being invoked.\r
709 @param PeiServices Describes the list of possible PEI Services.\r
710\r
711 @retval EFI_SUCCESS MpServicePpi is installed successfully.\r
712\r
713**/\r
714EFI_STATUS\r
715EFIAPI\r
716CpuMpPeimInit (\r
717 IN EFI_PEI_FILE_HANDLE FileHandle,\r
718 IN CONST EFI_PEI_SERVICES **PeiServices\r
719 )\r
720{\r
721 EFI_STATUS Status;\r
722 PEI_CPU_MP_DATA *PeiCpuMpData;\r
723 UINT32 ProcessorCount;\r
724\r
725 //\r
726 // Load new GDT table on BSP\r
727 //\r
728 AsmInitializeGdt (&mGdt);\r
729 //\r
730 // Get wakeup buffer and copy AP reset code in it\r
731 //\r
732 PeiCpuMpData = PrepareAPStartupVector ();\r
733 //\r
734 // Count processor number and collect processor information\r
735 //\r
736 ProcessorCount = CountProcessorNumber (PeiCpuMpData);\r
737 //\r
738 // Build location of PEI CPU MP DATA buffer in HOB\r
739 //\r
740 BuildGuidDataHob (\r
741 &gEfiCallerIdGuid,\r
742 (VOID *)&PeiCpuMpData,\r
743 sizeof(UINT64)\r
744 );\r
745 //\r
746 // Update and publish CPU BIST information\r
747 //\r
748 CollectBistDataFromPpi (PeiServices, PeiCpuMpData);\r
749 //\r
750 // register an event for EndOfPei\r
751 //\r
752 Status = PeiServicesNotifyPpi (&mNotifyList);\r
753 ASSERT_EFI_ERROR (Status);\r
754 //\r
755 // Install CPU MP PPI\r
756 //\r
757 Status = PeiServicesInstallPpi(&mPeiCpuMpPpiDesc);\r
758 ASSERT_EFI_ERROR (Status);\r
759\r
760 return Status;\r
761}\r