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