Home | History | Annotate | Download | only in CpuMpPei
      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