Home | History | Annotate | Download | only in CpuDxe
      1 /** @file
      2   CPU DXE Module.
      3 
      4   Copyright (c) 2008 - 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 "CpuDxe.h"
     16 #include "CpuMp.h"
     17 
     18 UINTN gMaxLogicalProcessorNumber;
     19 UINTN gApStackSize;
     20 UINTN gPollInterval = 100; // 100 microseconds
     21 
     22 MP_SYSTEM_DATA mMpSystemData;
     23 EFI_HANDLE     mMpServiceHandle       = NULL;
     24 EFI_EVENT      mExitBootServicesEvent = (EFI_EVENT)NULL;
     25 
     26 VOID *mCommonStack = 0;
     27 VOID *mTopOfApCommonStack = 0;
     28 VOID *mApStackStart = 0;
     29 
     30 volatile BOOLEAN mAPsAlreadyInitFinished = FALSE;
     31 volatile BOOLEAN mStopCheckAllAPsStatus = TRUE;
     32 
     33 EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate = {
     34   GetNumberOfProcessors,
     35   GetProcessorInfo,
     36   StartupAllAPs,
     37   StartupThisAP,
     38   SwitchBSP,
     39   EnableDisableAP,
     40   WhoAmI
     41 };
     42 
     43 /**
     44    Get Mp Service Lock.
     45 
     46   @param   CpuData    the pointer to CPU_DATA_BLOCK of specified processor
     47 
     48 **/
     49 VOID
     50 GetMpSpinLock (
     51   IN  CPU_DATA_BLOCK  *CpuData
     52   )
     53 {
     54   while (!AcquireSpinLockOrFail (&CpuData->CpuDataLock)) {
     55     CpuPause ();
     56   }
     57   CpuData->LockSelf = GetApicId ();
     58 }
     59 
     60 /**
     61    Release Mp Service Lock.
     62 
     63   @param   CpuData    the pointer to CPU_DATA_BLOCK of specified processor
     64 
     65 **/
     66 VOID
     67 ReleaseMpSpinLock (
     68   IN  CPU_DATA_BLOCK  *CpuData
     69   )
     70 {
     71   ReleaseSpinLock (&CpuData->CpuDataLock);
     72 }
     73 
     74 /**
     75   Check whether caller processor is BSP.
     76 
     77   @retval  TRUE       the caller is BSP
     78   @retval  FALSE      the caller is AP
     79 
     80 **/
     81 BOOLEAN
     82 IsBSP (
     83   VOID
     84   )
     85 {
     86   UINTN           CpuIndex;
     87   CPU_DATA_BLOCK  *CpuData;
     88 
     89   CpuData = NULL;
     90 
     91   WhoAmI (&mMpServicesTemplate, &CpuIndex);
     92   CpuData = &mMpSystemData.CpuDatas[CpuIndex];
     93 
     94   return CpuData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT ? TRUE : FALSE;
     95 }
     96 
     97 /**
     98   Get the Application Processors state.
     99 
    100   @param   CpuData    the pointer to CPU_DATA_BLOCK of specified AP
    101 
    102   @retval  CPU_STATE  the AP status
    103 
    104 **/
    105 CPU_STATE
    106 GetApState (
    107   IN  CPU_DATA_BLOCK  *CpuData
    108   )
    109 {
    110   CPU_STATE State;
    111 
    112   GetMpSpinLock (CpuData);
    113   State = CpuData->State;
    114   ReleaseMpSpinLock (CpuData);
    115 
    116   return State;
    117 }
    118 
    119 /**
    120   Set the Application Processors state.
    121 
    122   @param   CpuData    The pointer to CPU_DATA_BLOCK of specified AP
    123   @param   State      The AP status
    124 
    125 **/
    126 VOID
    127 SetApState (
    128   IN  CPU_DATA_BLOCK   *CpuData,
    129   IN  CPU_STATE        State
    130   )
    131 {
    132   GetMpSpinLock (CpuData);
    133   CpuData->State = State;
    134   ReleaseMpSpinLock (CpuData);
    135 }
    136 
    137 /**
    138   Set the Application Processor prepare to run a function specified
    139   by Params.
    140 
    141   @param CpuData           the pointer to CPU_DATA_BLOCK of specified AP
    142   @param Procedure         A pointer to the function to be run on enabled APs of the system
    143   @param ProcedureArgument Pointer to the optional parameter of the assigned function
    144 
    145 **/
    146 VOID
    147 SetApProcedure (
    148   IN   CPU_DATA_BLOCK        *CpuData,
    149   IN   EFI_AP_PROCEDURE      Procedure,
    150   IN   VOID                  *ProcedureArgument
    151   )
    152 {
    153   GetMpSpinLock (CpuData);
    154   CpuData->Parameter  = ProcedureArgument;
    155   CpuData->Procedure  = Procedure;
    156   ReleaseMpSpinLock (CpuData);
    157 }
    158 
    159 /**
    160   Check the Application Processors Status whether contains the Flags.
    161 
    162   @param     CpuData  the pointer to CPU_DATA_BLOCK of specified AP
    163   @param     Flags    the StatusFlag describing in EFI_PROCESSOR_INFORMATION
    164 
    165   @retval    TRUE     the AP status includes the StatusFlag
    166   @retval    FALSE    the AP status excludes the StatusFlag
    167 
    168 **/
    169 BOOLEAN
    170 TestCpuStatusFlag (
    171   IN  CPU_DATA_BLOCK  *CpuData,
    172   IN  UINT32          Flags
    173   )
    174 {
    175   UINT32 Ret;
    176 
    177   GetMpSpinLock (CpuData);
    178   Ret = CpuData->Info.StatusFlag & Flags;
    179   ReleaseMpSpinLock (CpuData);
    180 
    181   return (BOOLEAN) (Ret != 0);
    182 }
    183 
    184 /**
    185   Bitwise-Or of the Application Processors Status with the Flags.
    186 
    187   @param     CpuData  the pointer to CPU_DATA_BLOCK of specified AP
    188   @param     Flags    the StatusFlag describing in EFI_PROCESSOR_INFORMATION
    189 
    190 **/
    191 VOID
    192 CpuStatusFlagOr (
    193   IN  CPU_DATA_BLOCK  *CpuData,
    194   IN  UINT32          Flags
    195   )
    196 {
    197   GetMpSpinLock (CpuData);
    198   CpuData->Info.StatusFlag |= Flags;
    199   ReleaseMpSpinLock (CpuData);
    200 }
    201 
    202 /**
    203   Bitwise-AndNot of the Application Processors Status with the Flags.
    204 
    205   @param     CpuData  the pointer to CPU_DATA_BLOCK of specified AP
    206   @param     Flags    the StatusFlag describing in EFI_PROCESSOR_INFORMATION
    207 
    208 **/
    209 VOID
    210 CpuStatusFlagAndNot (
    211   IN  CPU_DATA_BLOCK  *CpuData,
    212   IN  UINT32          Flags
    213   )
    214 {
    215   GetMpSpinLock (CpuData);
    216   CpuData->Info.StatusFlag &= ~Flags;
    217   ReleaseMpSpinLock (CpuData);
    218 }
    219 
    220 /**
    221   Searches for the next blocking AP.
    222 
    223   Search for the next AP that is put in blocking state by single-threaded StartupAllAPs().
    224 
    225   @param  NextNumber           Pointer to the processor number of the next blocking AP.
    226 
    227   @retval EFI_SUCCESS          The next blocking AP has been found.
    228   @retval EFI_NOT_FOUND        No blocking AP exists.
    229 
    230 **/
    231 EFI_STATUS
    232 GetNextBlockedNumber (
    233   OUT UINTN  *NextNumber
    234   )
    235 {
    236   UINTN                 Number;
    237   CPU_STATE             CpuState;
    238   CPU_DATA_BLOCK        *CpuData;
    239 
    240   for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) {
    241     CpuData = &mMpSystemData.CpuDatas[Number];
    242     if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
    243       //
    244       // Skip BSP
    245       //
    246       continue;
    247     }
    248 
    249     CpuState = GetApState (CpuData);
    250     if (CpuState == CpuStateBlocked) {
    251       *NextNumber = Number;
    252       return EFI_SUCCESS;
    253     }
    254   }
    255 
    256   return EFI_NOT_FOUND;
    257 }
    258 
    259 /**
    260   Check if the APs state are finished, and update them to idle state
    261   by StartupAllAPs().
    262 
    263 **/
    264 VOID
    265 CheckAndUpdateAllAPsToIdleState (
    266   VOID
    267   )
    268 {
    269   UINTN                 ProcessorNumber;
    270   UINTN                 NextNumber;
    271   CPU_DATA_BLOCK        *CpuData;
    272   EFI_STATUS            Status;
    273   CPU_STATE             CpuState;
    274 
    275   for (ProcessorNumber = 0; ProcessorNumber < mMpSystemData.NumberOfProcessors; ProcessorNumber++) {
    276     CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
    277     if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
    278       //
    279       // Skip BSP
    280       //
    281       continue;
    282     }
    283 
    284     if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
    285       //
    286       // Skip Disabled processors
    287       //
    288       continue;
    289     }
    290 
    291     CpuState = GetApState (CpuData);
    292     if (CpuState == CpuStateFinished) {
    293       mMpSystemData.FinishCount++;
    294       if (mMpSystemData.SingleThread) {
    295         Status = GetNextBlockedNumber (&NextNumber);
    296         if (!EFI_ERROR (Status)) {
    297           SetApState (&mMpSystemData.CpuDatas[NextNumber], CpuStateReady);
    298           SetApProcedure (&mMpSystemData.CpuDatas[NextNumber],
    299                           mMpSystemData.Procedure,
    300                           mMpSystemData.ProcedureArgument);
    301           //
    302           // If this AP previous state is blocked, we should
    303           // wake up this AP by sent a SIPI. and avoid
    304           // re-involve the sleeping state. we must call
    305           // SetApProcedure() first.
    306           //
    307           ResetProcessorToIdleState (&mMpSystemData.CpuDatas[NextNumber]);
    308         }
    309       }
    310       SetApState (CpuData, CpuStateIdle);
    311     }
    312   }
    313 }
    314 
    315 /**
    316   Check if all APs are in state CpuStateSleeping.
    317 
    318   Return TRUE if all APs are in the CpuStateSleeping state.  Do not
    319   check the state of the BSP or any disabled APs.
    320 
    321   @retval TRUE   All APs are in CpuStateSleeping state.
    322   @retval FALSE  One or more APs are not in CpuStateSleeping state.
    323 
    324 **/
    325 BOOLEAN
    326 CheckAllAPsSleeping (
    327   VOID
    328   )
    329 {
    330   UINTN           ProcessorNumber;
    331   CPU_DATA_BLOCK  *CpuData;
    332 
    333   for (ProcessorNumber = 0; ProcessorNumber < mMpSystemData.NumberOfProcessors; ProcessorNumber++) {
    334     CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
    335     if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
    336       //
    337       // Skip BSP
    338       //
    339       continue;
    340     }
    341 
    342     if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
    343       //
    344       // Skip Disabled processors
    345       //
    346       continue;
    347     }
    348 
    349     if (GetApState (CpuData) != CpuStateSleeping) {
    350       return FALSE;
    351     }
    352   }
    353   return TRUE;
    354 }
    355 
    356 /**
    357   If the timeout expires before all APs returns from Procedure,
    358   we should forcibly terminate the executing AP and fill FailedList back
    359   by StartupAllAPs().
    360 
    361 **/
    362 VOID
    363 ResetAllFailedAPs (
    364   VOID
    365   )
    366 {
    367   CPU_DATA_BLOCK        *CpuData;
    368   UINTN                 Number;
    369   CPU_STATE             CpuState;
    370 
    371   if (mMpSystemData.FailedList != NULL) {
    372      *mMpSystemData.FailedList = AllocatePool ((mMpSystemData.StartCount - mMpSystemData.FinishCount + 1) * sizeof(UINTN));
    373      ASSERT (*mMpSystemData.FailedList != NULL);
    374   }
    375 
    376   for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) {
    377     CpuData = &mMpSystemData.CpuDatas[Number];
    378     if (TestCpuStatusFlag (CpuData,  PROCESSOR_AS_BSP_BIT)) {
    379       //
    380       // Skip BSP
    381       //
    382       continue;
    383     }
    384 
    385     if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
    386       //
    387       // Skip Disabled processors
    388       //
    389       continue;
    390     }
    391 
    392     CpuState = GetApState (CpuData);
    393     if (CpuState != CpuStateIdle &&
    394         CpuState != CpuStateSleeping) {
    395       if (mMpSystemData.FailedList != NULL) {
    396         (*mMpSystemData.FailedList)[mMpSystemData.FailedListIndex++] = Number;
    397       }
    398       ResetProcessorToIdleState (CpuData);
    399     }
    400   }
    401 
    402   if (mMpSystemData.FailedList != NULL) {
    403     (*mMpSystemData.FailedList)[mMpSystemData.FailedListIndex] = END_OF_CPU_LIST;
    404   }
    405 }
    406 
    407 /**
    408   This service retrieves the number of logical processor in the platform
    409   and the number of those logical processors that are enabled on this boot.
    410   This service may only be called from the BSP.
    411 
    412   This function is used to retrieve the following information:
    413     - The number of logical processors that are present in the system.
    414     - The number of enabled logical processors in the system at the instant
    415       this call is made.
    416 
    417   Because MP Service Protocol provides services to enable and disable processors
    418   dynamically, the number of enabled logical processors may vary during the
    419   course of a boot session.
    420 
    421   If this service is called from an AP, then EFI_DEVICE_ERROR is returned.
    422   If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then
    423   EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors
    424   is returned in NumberOfProcessors, the number of currently enabled processor
    425   is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
    426 
    427   @param[in]  This                        A pointer to the EFI_MP_SERVICES_PROTOCOL
    428                                           instance.
    429   @param[out] NumberOfProcessors          Pointer to the total number of logical
    430                                           processors in the system, including the BSP
    431                                           and disabled APs.
    432   @param[out] NumberOfEnabledProcessors   Pointer to the number of enabled logical
    433                                           processors that exist in system, including
    434                                           the BSP.
    435 
    436   @retval EFI_SUCCESS             The number of logical processors and enabled
    437                                   logical processors was retrieved.
    438   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
    439   @retval EFI_INVALID_PARAMETER   NumberOfProcessors is NULL.
    440   @retval EFI_INVALID_PARAMETER   NumberOfEnabledProcessors is NULL.
    441 
    442 **/
    443 EFI_STATUS
    444 EFIAPI
    445 GetNumberOfProcessors (
    446   IN  EFI_MP_SERVICES_PROTOCOL  *This,
    447   OUT UINTN                     *NumberOfProcessors,
    448   OUT UINTN                     *NumberOfEnabledProcessors
    449   )
    450 {
    451   if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
    452     return EFI_INVALID_PARAMETER;
    453   }
    454 
    455   if (!IsBSP ()) {
    456     return EFI_DEVICE_ERROR;
    457   }
    458 
    459   *NumberOfProcessors        = mMpSystemData.NumberOfProcessors;
    460   *NumberOfEnabledProcessors = mMpSystemData.NumberOfEnabledProcessors;
    461   return EFI_SUCCESS;
    462 }
    463 
    464 /**
    465   Gets detailed MP-related information on the requested processor at the
    466   instant this call is made. This service may only be called from the BSP.
    467 
    468   This service retrieves detailed MP-related information about any processor
    469   on the platform. Note the following:
    470     - The processor information may change during the course of a boot session.
    471     - The information presented here is entirely MP related.
    472 
    473   Information regarding the number of caches and their sizes, frequency of operation,
    474   slot numbers is all considered platform-related information and is not provided
    475   by this service.
    476 
    477   @param[in]  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL
    478                                     instance.
    479   @param[in]  ProcessorNumber       The handle number of processor.
    480   @param[out] ProcessorInfoBuffer   A pointer to the buffer where information for
    481                                     the requested processor is deposited.
    482 
    483   @retval EFI_SUCCESS             Processor information was returned.
    484   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
    485   @retval EFI_INVALID_PARAMETER   ProcessorInfoBuffer is NULL.
    486   @retval EFI_NOT_FOUND           The processor with the handle specified by
    487                                   ProcessorNumber does not exist in the platform.
    488 
    489 **/
    490 EFI_STATUS
    491 EFIAPI
    492 GetProcessorInfo (
    493   IN  EFI_MP_SERVICES_PROTOCOL   *This,
    494   IN  UINTN                      ProcessorNumber,
    495   OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
    496   )
    497 {
    498   if (ProcessorInfoBuffer == NULL) {
    499     return EFI_INVALID_PARAMETER;
    500   }
    501 
    502   if (!IsBSP ()) {
    503     return EFI_DEVICE_ERROR;
    504   }
    505 
    506   if (ProcessorNumber >= mMpSystemData.NumberOfProcessors) {
    507     return EFI_NOT_FOUND;
    508   }
    509 
    510   CopyMem (ProcessorInfoBuffer, &mMpSystemData.CpuDatas[ProcessorNumber], sizeof (EFI_PROCESSOR_INFORMATION));
    511   return EFI_SUCCESS;
    512 }
    513 
    514 /**
    515   This service executes a caller provided function on all enabled APs. APs can
    516   run either simultaneously or one at a time in sequence. This service supports
    517   both blocking and non-blocking requests. The non-blocking requests use EFI
    518   events so the BSP can detect when the APs have finished. This service may only
    519   be called from the BSP.
    520 
    521   This function is used to dispatch all the enabled APs to the function specified
    522   by Procedure.  If any enabled AP is busy, then EFI_NOT_READY is returned
    523   immediately and Procedure is not started on any AP.
    524 
    525   If SingleThread is TRUE, all the enabled APs execute the function specified by
    526   Procedure one by one, in ascending order of processor handle number. Otherwise,
    527   all the enabled APs execute the function specified by Procedure simultaneously.
    528 
    529   If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all
    530   APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in non-blocking
    531   mode, and the BSP returns from this service without waiting for APs. If a
    532   non-blocking mode is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
    533   is signaled, then EFI_UNSUPPORTED must be returned.
    534 
    535   If the timeout specified by TimeoutInMicroseconds expires before all APs return
    536   from Procedure, then Procedure on the failed APs is terminated. All enabled APs
    537   are always available for further calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
    538   and EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its
    539   content points to the list of processor handle numbers in which Procedure was
    540   terminated.
    541 
    542   Note: It is the responsibility of the consumer of the EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
    543   to make sure that the nature of the code that is executed on the BSP and the
    544   dispatched APs is well controlled. The MP Services Protocol does not guarantee
    545   that the Procedure function is MP-safe. Hence, the tasks that can be run in
    546   parallel are limited to certain independent tasks and well-controlled exclusive
    547   code. EFI services and protocols may not be called by APs unless otherwise
    548   specified.
    549 
    550   In blocking execution mode, BSP waits until all APs finish or
    551   TimeoutInMicroseconds expires.
    552 
    553   In non-blocking execution mode, BSP is freed to return to the caller and then
    554   proceed to the next task without having to wait for APs. The following
    555   sequence needs to occur in a non-blocking execution mode:
    556 
    557     -# The caller that intends to use this MP Services Protocol in non-blocking
    558        mode creates WaitEvent by calling the EFI CreateEvent() service.  The caller
    559        invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter WaitEvent
    560        is not NULL, then StartupAllAPs() executes in non-blocking mode. It requests
    561        the function specified by Procedure to be started on all the enabled APs,
    562        and releases the BSP to continue with other tasks.
    563     -# The caller can use the CheckEvent() and WaitForEvent() services to check
    564        the state of the WaitEvent created in step 1.
    565     -# When the APs complete their task or TimeoutInMicroSecondss expires, the MP
    566        Service signals WaitEvent by calling the EFI SignalEvent() function. If
    567        FailedCpuList is not NULL, its content is available when WaitEvent is
    568        signaled. If all APs returned from Procedure prior to the timeout, then
    569        FailedCpuList is set to NULL. If not all APs return from Procedure before
    570        the timeout, then FailedCpuList is filled in with the list of the failed
    571        APs. The buffer is allocated by MP Service Protocol using AllocatePool().
    572        It is the caller's responsibility to free the buffer with FreePool() service.
    573     -# This invocation of SignalEvent() function informs the caller that invoked
    574        EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs completed
    575        the specified task or a timeout occurred. The contents of FailedCpuList
    576        can be examined to determine which APs did not complete the specified task
    577        prior to the timeout.
    578 
    579   @param[in]  This                    A pointer to the EFI_MP_SERVICES_PROTOCOL
    580                                       instance.
    581   @param[in]  Procedure               A pointer to the function to be run on
    582                                       enabled APs of the system. See type
    583                                       EFI_AP_PROCEDURE.
    584   @param[in]  SingleThread            If TRUE, then all the enabled APs execute
    585                                       the function specified by Procedure one by
    586                                       one, in ascending order of processor handle
    587                                       number.  If FALSE, then all the enabled APs
    588                                       execute the function specified by Procedure
    589                                       simultaneously.
    590   @param[in]  WaitEvent               The event created by the caller with CreateEvent()
    591                                       service.  If it is NULL, then execute in
    592                                       blocking mode. BSP waits until all APs finish
    593                                       or TimeoutInMicroseconds expires.  If it's
    594                                       not NULL, then execute in non-blocking mode.
    595                                       BSP requests the function specified by
    596                                       Procedure to be started on all the enabled
    597                                       APs, and go on executing immediately. If
    598                                       all return from Procedure, or TimeoutInMicroseconds
    599                                       expires, this event is signaled. The BSP
    600                                       can use the CheckEvent() or WaitForEvent()
    601                                       services to check the state of event.  Type
    602                                       EFI_EVENT is defined in CreateEvent() in
    603                                       the Unified Extensible Firmware Interface
    604                                       Specification.
    605   @param[in]  TimeoutInMicroseconds   Indicates the time limit in microseconds for
    606                                       APs to return from Procedure, either for
    607                                       blocking or non-blocking mode. Zero means
    608                                       infinity.  If the timeout expires before
    609                                       all APs return from Procedure, then Procedure
    610                                       on the failed APs is terminated. All enabled
    611                                       APs are available for next function assigned
    612                                       by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
    613                                       or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
    614                                       If the timeout expires in blocking mode,
    615                                       BSP returns EFI_TIMEOUT.  If the timeout
    616                                       expires in non-blocking mode, WaitEvent
    617                                       is signaled with SignalEvent().
    618   @param[in]  ProcedureArgument       The parameter passed into Procedure for
    619                                       all APs.
    620   @param[out] FailedCpuList           If NULL, this parameter is ignored. Otherwise,
    621                                       if all APs finish successfully, then its
    622                                       content is set to NULL. If not all APs
    623                                       finish before timeout expires, then its
    624                                       content is set to address of the buffer
    625                                       holding handle numbers of the failed APs.
    626                                       The buffer is allocated by MP Service Protocol,
    627                                       and it's the caller's responsibility to
    628                                       free the buffer with FreePool() service.
    629                                       In blocking mode, it is ready for consumption
    630                                       when the call returns. In non-blocking mode,
    631                                       it is ready when WaitEvent is signaled.  The
    632                                       list of failed CPU is terminated by
    633                                       END_OF_CPU_LIST.
    634 
    635   @retval EFI_SUCCESS             In blocking mode, all APs have finished before
    636                                   the timeout expired.
    637   @retval EFI_SUCCESS             In non-blocking mode, function has been dispatched
    638                                   to all enabled APs.
    639   @retval EFI_UNSUPPORTED         A non-blocking mode request was made after the
    640                                   UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
    641                                   signaled.
    642   @retval EFI_DEVICE_ERROR        Caller processor is AP.
    643   @retval EFI_NOT_STARTED         No enabled APs exist in the system.
    644   @retval EFI_NOT_READY           Any enabled APs are busy.
    645   @retval EFI_TIMEOUT             In blocking mode, the timeout expired before
    646                                   all enabled APs have finished.
    647   @retval EFI_INVALID_PARAMETER   Procedure is NULL.
    648 
    649 **/
    650 EFI_STATUS
    651 EFIAPI
    652 StartupAllAPs (
    653   IN  EFI_MP_SERVICES_PROTOCOL  *This,
    654   IN  EFI_AP_PROCEDURE          Procedure,
    655   IN  BOOLEAN                   SingleThread,
    656   IN  EFI_EVENT                 WaitEvent               OPTIONAL,
    657   IN  UINTN                     TimeoutInMicroseconds,
    658   IN  VOID                      *ProcedureArgument      OPTIONAL,
    659   OUT UINTN                     **FailedCpuList         OPTIONAL
    660   )
    661 {
    662   EFI_STATUS            Status;
    663   CPU_DATA_BLOCK        *CpuData;
    664   UINTN                 Number;
    665   CPU_STATE             APInitialState;
    666   CPU_STATE             CpuState;
    667 
    668   CpuData = NULL;
    669 
    670   if (FailedCpuList != NULL) {
    671     *FailedCpuList = NULL;
    672   }
    673 
    674   if (!IsBSP ()) {
    675     return EFI_DEVICE_ERROR;
    676   }
    677 
    678   if (mMpSystemData.NumberOfProcessors == 1) {
    679     return EFI_NOT_STARTED;
    680   }
    681 
    682   if (Procedure == NULL) {
    683     return EFI_INVALID_PARAMETER;
    684   }
    685 
    686   //
    687   // temporarily stop checkAllAPsStatus for avoid resource dead-lock.
    688   //
    689   mStopCheckAllAPsStatus = TRUE;
    690 
    691   for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) {
    692     CpuData = &mMpSystemData.CpuDatas[Number];
    693     if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
    694       //
    695       // Skip BSP
    696       //
    697       continue;
    698     }
    699 
    700     if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
    701       //
    702       // Skip Disabled processors
    703       //
    704       continue;
    705     }
    706 
    707     CpuState = GetApState (CpuData);
    708     if (CpuState != CpuStateIdle &&
    709         CpuState != CpuStateSleeping) {
    710       return EFI_NOT_READY;
    711     }
    712   }
    713 
    714   mMpSystemData.Procedure         = Procedure;
    715   mMpSystemData.ProcedureArgument = ProcedureArgument;
    716   mMpSystemData.WaitEvent         = WaitEvent;
    717   mMpSystemData.Timeout           = TimeoutInMicroseconds;
    718   mMpSystemData.TimeoutActive     = (BOOLEAN) (TimeoutInMicroseconds != 0);
    719   mMpSystemData.FinishCount       = 0;
    720   mMpSystemData.StartCount        = 0;
    721   mMpSystemData.SingleThread      = SingleThread;
    722   mMpSystemData.FailedList        = FailedCpuList;
    723   mMpSystemData.FailedListIndex   = 0;
    724   APInitialState                  = CpuStateReady;
    725 
    726   for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) {
    727     CpuData = &mMpSystemData.CpuDatas[Number];
    728     if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
    729       //
    730       // Skip BSP
    731       //
    732       continue;
    733     }
    734 
    735     if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
    736       //
    737       // Skip Disabled processors
    738       //
    739       continue;
    740     }
    741 
    742     //
    743     // Get APs prepared, and put failing APs into FailedCpuList
    744     // if "SingleThread", only 1 AP will put to ready state, other AP will be put to ready
    745     // state 1 by 1, until the previous 1 finished its task
    746     // if not "SingleThread", all APs are put to ready state from the beginning
    747     //
    748     CpuState = GetApState (CpuData);
    749     if (CpuState == CpuStateIdle ||
    750         CpuState == CpuStateSleeping) {
    751       mMpSystemData.StartCount++;
    752 
    753       SetApState (CpuData, APInitialState);
    754 
    755       if (APInitialState == CpuStateReady) {
    756         SetApProcedure (CpuData, Procedure, ProcedureArgument);
    757         //
    758         // If this AP previous state is Sleeping, we should
    759         // wake up this AP by sent a SIPI. and avoid
    760         // re-involve the sleeping state. we must call
    761         // SetApProcedure() first.
    762         //
    763         if (CpuState == CpuStateSleeping) {
    764           ResetProcessorToIdleState (CpuData);
    765         }
    766       }
    767 
    768       if (SingleThread) {
    769         APInitialState = CpuStateBlocked;
    770       }
    771     }
    772   }
    773 
    774   mStopCheckAllAPsStatus = FALSE;
    775 
    776   if (WaitEvent != NULL) {
    777     //
    778     // non blocking
    779     //
    780     return EFI_SUCCESS;
    781   }
    782 
    783   //
    784   // Blocking temporarily stop CheckAllAPsStatus()
    785   //
    786   mStopCheckAllAPsStatus = TRUE;
    787 
    788   while (TRUE) {
    789     CheckAndUpdateAllAPsToIdleState ();
    790     if (mMpSystemData.FinishCount == mMpSystemData.StartCount) {
    791       Status = EFI_SUCCESS;
    792       goto Done;
    793     }
    794 
    795     //
    796     // task timeout
    797     //
    798     if (mMpSystemData.TimeoutActive && mMpSystemData.Timeout < 0) {
    799       ResetAllFailedAPs();
    800       Status = EFI_TIMEOUT;
    801       goto Done;
    802     }
    803 
    804     MicroSecondDelay (gPollInterval);
    805     mMpSystemData.Timeout -= gPollInterval;
    806   }
    807 
    808 Done:
    809 
    810   return Status;
    811 }
    812 
    813 /**
    814   This service lets the caller get one enabled AP to execute a caller-provided
    815   function. The caller can request the BSP to either wait for the completion
    816   of the AP or just proceed with the next task by using the EFI event mechanism.
    817   See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking
    818   execution support.  This service may only be called from the BSP.
    819 
    820   This function is used to dispatch one enabled AP to the function specified by
    821   Procedure passing in the argument specified by ProcedureArgument.  If WaitEvent
    822   is NULL, execution is in blocking mode. The BSP waits until the AP finishes or
    823   TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode.
    824   BSP proceeds to the next task without waiting for the AP. If a non-blocking mode
    825   is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled,
    826   then EFI_UNSUPPORTED must be returned.
    827 
    828   If the timeout specified by TimeoutInMicroseconds expires before the AP returns
    829   from Procedure, then execution of Procedure by the AP is terminated. The AP is
    830   available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and
    831   EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
    832 
    833   @param[in]  This                    A pointer to the EFI_MP_SERVICES_PROTOCOL
    834                                       instance.
    835   @param[in]  Procedure               A pointer to the function to be run on
    836                                       enabled APs of the system. See type
    837                                       EFI_AP_PROCEDURE.
    838   @param[in]  ProcessorNumber         The handle number of the AP. The range is
    839                                       from 0 to the total number of logical
    840                                       processors minus 1. The total number of
    841                                       logical processors can be retrieved by
    842                                       EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
    843   @param[in]  WaitEvent               The event created by the caller with CreateEvent()
    844                                       service.  If it is NULL, then execute in
    845                                       blocking mode. BSP waits until all APs finish
    846                                       or TimeoutInMicroseconds expires.  If it's
    847                                       not NULL, then execute in non-blocking mode.
    848                                       BSP requests the function specified by
    849                                       Procedure to be started on all the enabled
    850                                       APs, and go on executing immediately. If
    851                                       all return from Procedure or TimeoutInMicroseconds
    852                                       expires, this event is signaled. The BSP
    853                                       can use the CheckEvent() or WaitForEvent()
    854                                       services to check the state of event.  Type
    855                                       EFI_EVENT is defined in CreateEvent() in
    856                                       the Unified Extensible Firmware Interface
    857                                       Specification.
    858   @param[in]  TimeoutInMicroseconds   Indicates the time limit in microseconds for
    859                                       APs to return from Procedure, either for
    860                                       blocking or non-blocking mode. Zero means
    861                                       infinity.  If the timeout expires before
    862                                       all APs return from Procedure, then Procedure
    863                                       on the failed APs is terminated. All enabled
    864                                       APs are available for next function assigned
    865                                       by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
    866                                       or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
    867                                       If the timeout expires in blocking mode,
    868                                       BSP returns EFI_TIMEOUT.  If the timeout
    869                                       expires in non-blocking mode, WaitEvent
    870                                       is signaled with SignalEvent().
    871   @param[in]  ProcedureArgument       The parameter passed into Procedure for
    872                                       all APs.
    873   @param[out] Finished                If NULL, this parameter is ignored.  In
    874                                       blocking mode, this parameter is ignored.
    875                                       In non-blocking mode, if AP returns from
    876                                       Procedure before the timeout expires, its
    877                                       content is set to TRUE. Otherwise, the
    878                                       value is set to FALSE. The caller can
    879                                       determine if the AP returned from Procedure
    880                                       by evaluating this value.
    881 
    882   @retval EFI_SUCCESS             In blocking mode, specified AP finished before
    883                                   the timeout expires.
    884   @retval EFI_SUCCESS             In non-blocking mode, the function has been
    885                                   dispatched to specified AP.
    886   @retval EFI_UNSUPPORTED         A non-blocking mode request was made after the
    887                                   UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
    888                                   signaled.
    889   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
    890   @retval EFI_TIMEOUT             In blocking mode, the timeout expired before
    891                                   the specified AP has finished.
    892   @retval EFI_NOT_READY           The specified AP is busy.
    893   @retval EFI_NOT_FOUND           The processor with the handle specified by
    894                                   ProcessorNumber does not exist.
    895   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP or disabled AP.
    896   @retval EFI_INVALID_PARAMETER   Procedure is NULL.
    897 
    898 **/
    899 EFI_STATUS
    900 EFIAPI
    901 StartupThisAP (
    902   IN  EFI_MP_SERVICES_PROTOCOL  *This,
    903   IN  EFI_AP_PROCEDURE          Procedure,
    904   IN  UINTN                     ProcessorNumber,
    905   IN  EFI_EVENT                 WaitEvent               OPTIONAL,
    906   IN  UINTN                     TimeoutInMicroseconds,
    907   IN  VOID                      *ProcedureArgument      OPTIONAL,
    908   OUT BOOLEAN                   *Finished               OPTIONAL
    909   )
    910 {
    911   CPU_DATA_BLOCK        *CpuData;
    912   CPU_STATE             CpuState;
    913 
    914   CpuData = NULL;
    915 
    916   if (Finished != NULL) {
    917     *Finished = FALSE;
    918   }
    919 
    920   if (!IsBSP ()) {
    921     return EFI_DEVICE_ERROR;
    922   }
    923 
    924   if (Procedure == NULL) {
    925     return EFI_INVALID_PARAMETER;
    926   }
    927 
    928   if (ProcessorNumber >= mMpSystemData.NumberOfProcessors) {
    929     return EFI_NOT_FOUND;
    930   }
    931 
    932   //
    933   // temporarily stop checkAllAPsStatus for avoid resource dead-lock.
    934   //
    935   mStopCheckAllAPsStatus = TRUE;
    936 
    937   CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
    938   if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT) ||
    939       !TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
    940     return EFI_INVALID_PARAMETER;
    941   }
    942 
    943   CpuState = GetApState (CpuData);
    944   if (CpuState != CpuStateIdle &&
    945       CpuState != CpuStateSleeping) {
    946     return EFI_NOT_READY;
    947   }
    948 
    949   SetApState (CpuData, CpuStateReady);
    950 
    951   SetApProcedure (CpuData, Procedure, ProcedureArgument);
    952   //
    953   // If this AP previous state is Sleeping, we should
    954   // wake up this AP by sent a SIPI. and avoid
    955   // re-involve the sleeping state. we must call
    956   // SetApProcedure() first.
    957   //
    958   if (CpuState == CpuStateSleeping) {
    959     ResetProcessorToIdleState (CpuData);
    960   }
    961 
    962   CpuData->Timeout = TimeoutInMicroseconds;
    963   CpuData->WaitEvent = WaitEvent;
    964   CpuData->TimeoutActive = (BOOLEAN) (TimeoutInMicroseconds != 0);
    965   CpuData->Finished = Finished;
    966 
    967   mStopCheckAllAPsStatus = FALSE;
    968 
    969   if (WaitEvent != NULL) {
    970     //
    971     // Non Blocking
    972     //
    973     return EFI_SUCCESS;
    974   }
    975 
    976   //
    977   // Blocking
    978   //
    979   while (TRUE) {
    980     if (GetApState (CpuData) == CpuStateFinished) {
    981       SetApState (CpuData, CpuStateIdle);
    982       break;
    983     }
    984 
    985     if (CpuData->TimeoutActive && CpuData->Timeout < 0) {
    986       ResetProcessorToIdleState (CpuData);
    987       return EFI_TIMEOUT;
    988     }
    989 
    990     MicroSecondDelay (gPollInterval);
    991     CpuData->Timeout -= gPollInterval;
    992   }
    993 
    994   return EFI_SUCCESS;
    995 }
    996 
    997 /**
    998   This service switches the requested AP to be the BSP from that point onward.
    999   This service changes the BSP for all purposes.   This call can only be performed
   1000   by the current BSP.
   1001 
   1002   This service switches the requested AP to be the BSP from that point onward.
   1003   This service changes the BSP for all purposes. The new BSP can take over the
   1004   execution of the old BSP and continue seamlessly from where the old one left
   1005   off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
   1006   is signaled.
   1007 
   1008   If the BSP cannot be switched prior to the return from this service, then
   1009   EFI_UNSUPPORTED must be returned.
   1010 
   1011   @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
   1012   @param[in] ProcessorNumber   The handle number of AP that is to become the new
   1013                                BSP. The range is from 0 to the total number of
   1014                                logical processors minus 1. The total number of
   1015                                logical processors can be retrieved by
   1016                                EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
   1017   @param[in] EnableOldBSP      If TRUE, then the old BSP will be listed as an
   1018                                enabled AP. Otherwise, it will be disabled.
   1019 
   1020   @retval EFI_SUCCESS             BSP successfully switched.
   1021   @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to
   1022                                   this service returning.
   1023   @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
   1024   @retval EFI_SUCCESS             The calling processor is an AP.
   1025   @retval EFI_NOT_FOUND           The processor with the handle specified by
   1026                                   ProcessorNumber does not exist.
   1027   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the current BSP or
   1028                                   a disabled AP.
   1029   @retval EFI_NOT_READY           The specified AP is busy.
   1030 
   1031 **/
   1032 EFI_STATUS
   1033 EFIAPI
   1034 SwitchBSP (
   1035   IN EFI_MP_SERVICES_PROTOCOL  *This,
   1036   IN  UINTN                    ProcessorNumber,
   1037   IN  BOOLEAN                  EnableOldBSP
   1038   )
   1039 {
   1040    //
   1041    // Current always return unsupported.
   1042    //
   1043    return EFI_UNSUPPORTED;
   1044 }
   1045 
   1046 /**
   1047   This service lets the caller enable or disable an AP from this point onward.
   1048   This service may only be called from the BSP.
   1049 
   1050   This service allows the caller enable or disable an AP from this point onward.
   1051   The caller can optionally specify the health status of the AP by Health. If
   1052   an AP is being disabled, then the state of the disabled AP is implementation
   1053   dependent. If an AP is enabled, then the implementation must guarantee that a
   1054   complete initialization sequence is performed on the AP, so the AP is in a state
   1055   that is compatible with an MP operating system. This service may not be supported
   1056   after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled.
   1057 
   1058   If the enable or disable AP operation cannot be completed prior to the return
   1059   from this service, then EFI_UNSUPPORTED must be returned.
   1060 
   1061   @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
   1062   @param[in] ProcessorNumber   The handle number of AP that is to become the new
   1063                                BSP. The range is from 0 to the total number of
   1064                                logical processors minus 1. The total number of
   1065                                logical processors can be retrieved by
   1066                                EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
   1067   @param[in] EnableAP          Specifies the new state for the processor for
   1068                                enabled, FALSE for disabled.
   1069   @param[in] HealthFlag        If not NULL, a pointer to a value that specifies
   1070                                the new health status of the AP. This flag
   1071                                corresponds to StatusFlag defined in
   1072                                EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
   1073                                the PROCESSOR_HEALTH_STATUS_BIT is used. All other
   1074                                bits are ignored.  If it is NULL, this parameter
   1075                                is ignored.
   1076 
   1077   @retval EFI_SUCCESS             The specified AP was enabled or disabled successfully.
   1078   @retval EFI_UNSUPPORTED         Enabling or disabling an AP cannot be completed
   1079                                   prior to this service returning.
   1080   @retval EFI_UNSUPPORTED         Enabling or disabling an AP is not supported.
   1081   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
   1082   @retval EFI_NOT_FOUND           Processor with the handle specified by ProcessorNumber
   1083                                   does not exist.
   1084   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP.
   1085 
   1086 **/
   1087 EFI_STATUS
   1088 EFIAPI
   1089 EnableDisableAP (
   1090   IN  EFI_MP_SERVICES_PROTOCOL  *This,
   1091   IN  UINTN                     ProcessorNumber,
   1092   IN  BOOLEAN                   EnableAP,
   1093   IN  UINT32                    *HealthFlag OPTIONAL
   1094   )
   1095 {
   1096   CPU_DATA_BLOCK *CpuData;
   1097   BOOLEAN        TempStopCheckState;
   1098   CPU_STATE      CpuState;
   1099 
   1100   CpuData = NULL;
   1101   TempStopCheckState = FALSE;
   1102 
   1103   if (!IsBSP ()) {
   1104     return EFI_DEVICE_ERROR;
   1105   }
   1106 
   1107   if (ProcessorNumber >= mMpSystemData.NumberOfProcessors) {
   1108     return EFI_NOT_FOUND;
   1109   }
   1110 
   1111   //
   1112   // temporarily stop checkAllAPsStatus for initialize parameters.
   1113   //
   1114   if (!mStopCheckAllAPsStatus) {
   1115     mStopCheckAllAPsStatus = TRUE;
   1116     TempStopCheckState = TRUE;
   1117   }
   1118 
   1119   CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
   1120   if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
   1121     return EFI_INVALID_PARAMETER;
   1122   }
   1123 
   1124   CpuState = GetApState (CpuData);
   1125   if (CpuState != CpuStateIdle &&
   1126       CpuState != CpuStateSleeping) {
   1127     return EFI_UNSUPPORTED;
   1128   }
   1129 
   1130   if (EnableAP) {
   1131     if (!(TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT))) {
   1132       mMpSystemData.NumberOfEnabledProcessors++;
   1133     }
   1134     CpuStatusFlagOr (CpuData, PROCESSOR_ENABLED_BIT);
   1135   } else {
   1136     if (TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
   1137       mMpSystemData.NumberOfEnabledProcessors--;
   1138     }
   1139     CpuStatusFlagAndNot (CpuData, PROCESSOR_ENABLED_BIT);
   1140   }
   1141 
   1142   if (HealthFlag != NULL) {
   1143     CpuStatusFlagAndNot (CpuData, (UINT32)~PROCESSOR_HEALTH_STATUS_BIT);
   1144     CpuStatusFlagOr (CpuData, (*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT));
   1145   }
   1146 
   1147   if (TempStopCheckState) {
   1148     mStopCheckAllAPsStatus = FALSE;
   1149   }
   1150 
   1151   return EFI_SUCCESS;
   1152 }
   1153 
   1154 /**
   1155   This return the handle number for the calling processor.  This service may be
   1156   called from the BSP and APs.
   1157 
   1158   This service returns the processor handle number for the calling processor.
   1159   The returned value is in the range from 0 to the total number of logical
   1160   processors minus 1. The total number of logical processors can be retrieved
   1161   with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be
   1162   called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER
   1163   is returned. Otherwise, the current processors handle number is returned in
   1164   ProcessorNumber, and EFI_SUCCESS is returned.
   1165 
   1166   @param[in]  This             A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
   1167   @param[out] ProcessorNumber  The handle number of AP that is to become the new
   1168                                BSP. The range is from 0 to the total number of
   1169                                logical processors minus 1. The total number of
   1170                                logical processors can be retrieved by
   1171                                EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
   1172 
   1173   @retval EFI_SUCCESS             The current processor handle number was returned
   1174                                   in ProcessorNumber.
   1175   @retval EFI_INVALID_PARAMETER   ProcessorNumber is NULL.
   1176 
   1177 **/
   1178 EFI_STATUS
   1179 EFIAPI
   1180 WhoAmI (
   1181   IN EFI_MP_SERVICES_PROTOCOL  *This,
   1182   OUT UINTN                    *ProcessorNumber
   1183   )
   1184 {
   1185   UINTN   Index;
   1186   UINT32  ProcessorId;
   1187 
   1188   if (ProcessorNumber == NULL) {
   1189     return EFI_INVALID_PARAMETER;
   1190   }
   1191 
   1192   ProcessorId = GetApicId ();
   1193   for (Index = 0; Index < mMpSystemData.NumberOfProcessors; Index++) {
   1194     if (mMpSystemData.CpuDatas[Index].Info.ProcessorId == ProcessorId) {
   1195       break;
   1196     }
   1197   }
   1198 
   1199   *ProcessorNumber = Index;
   1200   return EFI_SUCCESS;
   1201 }
   1202 
   1203 /**
   1204   Terminate AP's task and set it to idle state.
   1205 
   1206   This function terminates AP's task due to timeout by sending INIT-SIPI,
   1207   and sends it to idle state.
   1208 
   1209   @param CpuData           the pointer to CPU_DATA_BLOCK of specified AP
   1210 
   1211 **/
   1212 VOID
   1213 ResetProcessorToIdleState (
   1214   IN CPU_DATA_BLOCK  *CpuData
   1215   )
   1216 {
   1217   ResetApStackless ((UINT32)CpuData->Info.ProcessorId);
   1218 }
   1219 
   1220 /**
   1221   Application Processors do loop routine
   1222   after switch to its own stack.
   1223 
   1224   @param  Context1    A pointer to the context to pass into the function.
   1225   @param  Context2    A pointer to the context to pass into the function.
   1226 
   1227 **/
   1228 VOID
   1229 ProcessorToIdleState (
   1230   IN      VOID                      *Context1,  OPTIONAL
   1231   IN      VOID                      *Context2   OPTIONAL
   1232   )
   1233 {
   1234   UINTN                 ProcessorNumber;
   1235   CPU_DATA_BLOCK        *CpuData;
   1236   EFI_AP_PROCEDURE      Procedure;
   1237   volatile VOID         *ProcedureArgument;
   1238 
   1239   AsmApDoneWithCommonStack ();
   1240 
   1241   while (!mAPsAlreadyInitFinished) {
   1242     CpuPause ();
   1243   }
   1244 
   1245   WhoAmI (&mMpServicesTemplate, &ProcessorNumber);
   1246   CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
   1247 
   1248   //
   1249   // Avoid forcibly reset AP caused the AP got lock not release.
   1250   //
   1251   if (CpuData->LockSelf == (INTN) GetApicId ()) {
   1252     ReleaseSpinLock (&CpuData->CpuDataLock);
   1253   }
   1254 
   1255   //
   1256   // Avoid forcibly reset AP caused the timeout AP State is not
   1257   // updated.
   1258   //
   1259   GetMpSpinLock (CpuData);
   1260   if (CpuData->State == CpuStateBusy) {
   1261     CpuData->Procedure = NULL;
   1262   }
   1263   CpuData->State = CpuStateIdle;
   1264   ReleaseMpSpinLock (CpuData);
   1265 
   1266   while (TRUE) {
   1267     GetMpSpinLock (CpuData);
   1268     ProcedureArgument = CpuData->Parameter;
   1269     Procedure = CpuData->Procedure;
   1270     ReleaseMpSpinLock (CpuData);
   1271 
   1272     if (Procedure != NULL) {
   1273       SetApState (CpuData, CpuStateBusy);
   1274 
   1275       Procedure ((VOID*) ProcedureArgument);
   1276 
   1277       GetMpSpinLock (CpuData);
   1278       CpuData->Procedure = NULL;
   1279       CpuData->State = CpuStateFinished;
   1280       ReleaseMpSpinLock (CpuData);
   1281     } else {
   1282       //
   1283       // if no procedure to execution, we simply put AP
   1284       // into sleeping state, and waiting BSP sent SIPI.
   1285       //
   1286       GetMpSpinLock (CpuData);
   1287       if (CpuData->State == CpuStateIdle) {
   1288           CpuData->State = CpuStateSleeping;
   1289       }
   1290       ReleaseMpSpinLock (CpuData);
   1291     }
   1292 
   1293     if (GetApState (CpuData) == CpuStateSleeping) {
   1294       CpuSleep ();
   1295     }
   1296 
   1297     CpuPause ();
   1298   }
   1299 
   1300   CpuSleep ();
   1301   CpuDeadLoop ();
   1302 }
   1303 
   1304 /**
   1305   Checks AP' status periodically.
   1306 
   1307   This function is triggerred by timer perodically to check the
   1308   state of AP forStartupThisAP() executed in non-blocking mode.
   1309 
   1310   @param  Event    Event triggered.
   1311   @param  Context  Parameter passed with the event.
   1312 
   1313 **/
   1314 VOID
   1315 EFIAPI
   1316 CheckThisAPStatus (
   1317   IN  EFI_EVENT        Event,
   1318   IN  VOID             *Context
   1319   )
   1320 {
   1321   CPU_DATA_BLOCK  *CpuData;
   1322   CPU_STATE       CpuState;
   1323 
   1324   CpuData = (CPU_DATA_BLOCK *) Context;
   1325   if (CpuData->TimeoutActive) {
   1326     CpuData->Timeout -= gPollInterval;
   1327   }
   1328 
   1329   CpuState = GetApState (CpuData);
   1330 
   1331   if (CpuState == CpuStateFinished) {
   1332     if (CpuData->Finished) {
   1333       *CpuData->Finished = TRUE;
   1334     }
   1335     SetApState (CpuData, CpuStateIdle);
   1336     goto out;
   1337   }
   1338 
   1339   if (CpuData->TimeoutActive && CpuData->Timeout < 0) {
   1340     if (CpuState != CpuStateIdle &&
   1341         CpuData->Finished) {
   1342       *CpuData->Finished = FALSE;
   1343     }
   1344     ResetProcessorToIdleState (CpuData);
   1345     goto out;
   1346   }
   1347 
   1348   return;
   1349 
   1350 out:
   1351   CpuData->TimeoutActive = FALSE;
   1352   gBS->SignalEvent (CpuData->WaitEvent);
   1353   CpuData->WaitEvent = NULL;
   1354 }
   1355 
   1356 /**
   1357   Checks APs' status periodically.
   1358 
   1359   This function is triggerred by timer perodically to check the
   1360   state of APs for StartupAllAPs() executed in non-blocking mode.
   1361 
   1362   @param  Event    Event triggered.
   1363   @param  Context  Parameter passed with the event.
   1364 
   1365 **/
   1366 VOID
   1367 EFIAPI
   1368 CheckAllAPsStatus (
   1369   IN  EFI_EVENT        Event,
   1370   IN  VOID             *Context
   1371   )
   1372 {
   1373   CPU_DATA_BLOCK *CpuData;
   1374   UINTN          Number;
   1375   EFI_STATUS     Status;
   1376 
   1377   if (mMpSystemData.TimeoutActive) {
   1378     mMpSystemData.Timeout -= gPollInterval;
   1379   }
   1380 
   1381   if (mStopCheckAllAPsStatus) {
   1382     return;
   1383   }
   1384 
   1385   //
   1386   // avoid next timer enter.
   1387   //
   1388   Status = gBS->SetTimer (
   1389                   mMpSystemData.CheckAllAPsEvent,
   1390                   TimerCancel,
   1391                   0
   1392                   );
   1393   ASSERT_EFI_ERROR (Status);
   1394 
   1395   if (mMpSystemData.WaitEvent != NULL) {
   1396     CheckAndUpdateAllAPsToIdleState ();
   1397     //
   1398     // task timeout
   1399     //
   1400     if (mMpSystemData.TimeoutActive && mMpSystemData.Timeout < 0) {
   1401       ResetAllFailedAPs();
   1402       //
   1403       // force exit
   1404       //
   1405       mMpSystemData.FinishCount = mMpSystemData.StartCount;
   1406     }
   1407 
   1408     if (mMpSystemData.FinishCount != mMpSystemData.StartCount) {
   1409       goto EXIT;
   1410     }
   1411 
   1412     mMpSystemData.TimeoutActive = FALSE;
   1413     gBS->SignalEvent (mMpSystemData.WaitEvent);
   1414     mMpSystemData.WaitEvent = NULL;
   1415     mStopCheckAllAPsStatus = TRUE;
   1416 
   1417     goto EXIT;
   1418   }
   1419 
   1420   //
   1421   // check each AP status for StartupThisAP
   1422   //
   1423   for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) {
   1424     CpuData = &mMpSystemData.CpuDatas[Number];
   1425     if (CpuData->WaitEvent) {
   1426       CheckThisAPStatus (NULL, (VOID *)CpuData);
   1427     }
   1428   }
   1429 
   1430 EXIT:
   1431   Status = gBS->SetTimer (
   1432                   mMpSystemData.CheckAllAPsEvent,
   1433                   TimerPeriodic,
   1434                   EFI_TIMER_PERIOD_MICROSECONDS (100)
   1435                   );
   1436   ASSERT_EFI_ERROR (Status);
   1437 }
   1438 
   1439 /**
   1440   Application Processor C code entry point.
   1441 
   1442 **/
   1443 VOID
   1444 EFIAPI
   1445 ApEntryPointInC (
   1446   VOID
   1447   )
   1448 {
   1449   VOID*           TopOfApStack;
   1450   UINTN           ProcessorNumber;
   1451 
   1452   if (!mAPsAlreadyInitFinished) {
   1453     FillInProcessorInformation (FALSE, mMpSystemData.NumberOfProcessors);
   1454     TopOfApStack  = (UINT8*)mApStackStart + gApStackSize;
   1455     mApStackStart = TopOfApStack;
   1456 
   1457     //
   1458     // Store the Stack address, when reset the AP, We can found the original address.
   1459     //
   1460     mMpSystemData.CpuDatas[mMpSystemData.NumberOfProcessors].TopOfStack = TopOfApStack;
   1461     mMpSystemData.NumberOfProcessors++;
   1462     mMpSystemData.NumberOfEnabledProcessors++;
   1463   } else {
   1464     WhoAmI (&mMpServicesTemplate, &ProcessorNumber);
   1465     //
   1466     // Get the original stack address.
   1467     //
   1468     TopOfApStack = mMpSystemData.CpuDatas[ProcessorNumber].TopOfStack;
   1469   }
   1470 
   1471   SwitchStack (
   1472     (SWITCH_STACK_ENTRY_POINT)(UINTN)ProcessorToIdleState,
   1473     NULL,
   1474     NULL,
   1475     TopOfApStack);
   1476 }
   1477 
   1478 /**
   1479   This function is called by all processors (both BSP and AP) once and collects MP related data.
   1480 
   1481   @param Bsp             TRUE if the CPU is BSP
   1482   @param ProcessorNumber The specific processor number
   1483 
   1484   @retval EFI_SUCCESS    Data for the processor collected and filled in
   1485 
   1486 **/
   1487 EFI_STATUS
   1488 FillInProcessorInformation (
   1489   IN     BOOLEAN              Bsp,
   1490   IN     UINTN                ProcessorNumber
   1491   )
   1492 {
   1493   CPU_DATA_BLOCK  *CpuData;
   1494   UINT32          ProcessorId;
   1495 
   1496   CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
   1497   ProcessorId  = GetApicId ();
   1498   CpuData->Info.ProcessorId  = ProcessorId;
   1499   CpuData->Info.StatusFlag   = PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT;
   1500   if (Bsp) {
   1501     CpuData->Info.StatusFlag |= PROCESSOR_AS_BSP_BIT;
   1502   }
   1503   CpuData->Info.Location.Package = ProcessorId;
   1504   CpuData->Info.Location.Core    = 0;
   1505   CpuData->Info.Location.Thread  = 0;
   1506   CpuData->State = Bsp ? CpuStateBusy : CpuStateIdle;
   1507 
   1508   CpuData->Procedure        = NULL;
   1509   CpuData->Parameter        = NULL;
   1510   InitializeSpinLock (&CpuData->CpuDataLock);
   1511   CpuData->LockSelf         = -1;
   1512 
   1513   return EFI_SUCCESS;
   1514 }
   1515 
   1516 /**
   1517   Prepare the System Data.
   1518 
   1519   @retval EFI_SUCCESS     the System Data finished initilization.
   1520 
   1521 **/
   1522 EFI_STATUS
   1523 InitMpSystemData (
   1524   VOID
   1525   )
   1526 {
   1527   EFI_STATUS     Status;
   1528 
   1529   ZeroMem (&mMpSystemData, sizeof (MP_SYSTEM_DATA));
   1530 
   1531   mMpSystemData.NumberOfProcessors = 1;
   1532   mMpSystemData.NumberOfEnabledProcessors = 1;
   1533 
   1534   mMpSystemData.CpuDatas = AllocateZeroPool (sizeof (CPU_DATA_BLOCK) * gMaxLogicalProcessorNumber);
   1535   ASSERT(mMpSystemData.CpuDatas != NULL);
   1536 
   1537   Status = gBS->CreateEvent (
   1538                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
   1539                   TPL_CALLBACK,
   1540                   CheckAllAPsStatus,
   1541                   NULL,
   1542                   &mMpSystemData.CheckAllAPsEvent
   1543                   );
   1544   ASSERT_EFI_ERROR (Status);
   1545 
   1546   //
   1547   // Set timer to check all APs status.
   1548   //
   1549   Status = gBS->SetTimer (
   1550                   mMpSystemData.CheckAllAPsEvent,
   1551                   TimerPeriodic,
   1552                   EFI_TIMER_PERIOD_MICROSECONDS (100)
   1553                   );
   1554   ASSERT_EFI_ERROR (Status);
   1555 
   1556   //
   1557   // BSP
   1558   //
   1559   FillInProcessorInformation (TRUE, 0);
   1560 
   1561   return EFI_SUCCESS;
   1562 }
   1563 
   1564 /**
   1565   Collects BIST data from HOB.
   1566 
   1567   This function collects BIST data from HOB built from Sec Platform Information
   1568   PPI or SEC Platform Information2 PPI.
   1569 
   1570 **/
   1571 VOID
   1572 CollectBistDataFromHob (
   1573   VOID
   1574   )
   1575 {
   1576   EFI_HOB_GUID_TYPE                     *GuidHob;
   1577   EFI_SEC_PLATFORM_INFORMATION_RECORD2  *SecPlatformInformation2;
   1578   EFI_SEC_PLATFORM_INFORMATION_RECORD   *SecPlatformInformation;
   1579   UINTN                                 NumberOfData;
   1580   EFI_SEC_PLATFORM_INFORMATION_CPU      *CpuInstance;
   1581   EFI_SEC_PLATFORM_INFORMATION_CPU      BspCpuInstance;
   1582   UINTN                                 ProcessorNumber;
   1583   UINT32                                InitialLocalApicId;
   1584   CPU_DATA_BLOCK                        *CpuData;
   1585 
   1586   SecPlatformInformation2 = NULL;
   1587   SecPlatformInformation  = NULL;
   1588 
   1589   //
   1590   // Get gEfiSecPlatformInformation2PpiGuid Guided HOB firstly
   1591   //
   1592   GuidHob = GetFirstGuidHob (&gEfiSecPlatformInformation2PpiGuid);
   1593   if (GuidHob != NULL) {
   1594     //
   1595     // Sec Platform Information2 PPI includes BSP/APs' BIST information
   1596     //
   1597     SecPlatformInformation2 = GET_GUID_HOB_DATA (GuidHob);
   1598     NumberOfData = SecPlatformInformation2->NumberOfCpus;
   1599     CpuInstance  = SecPlatformInformation2->CpuInstance;
   1600   } else {
   1601     //
   1602     // Otherwise, get gEfiSecPlatformInformationPpiGuid Guided HOB
   1603     //
   1604     GuidHob = GetFirstGuidHob (&gEfiSecPlatformInformationPpiGuid);
   1605     if (GuidHob != NULL) {
   1606       SecPlatformInformation = GET_GUID_HOB_DATA (GuidHob);
   1607       NumberOfData = 1;
   1608       //
   1609       // SEC Platform Information only includes BSP's BIST information
   1610       // does not have BSP's APIC ID
   1611       //
   1612       BspCpuInstance.CpuLocation = GetApicId ();
   1613       BspCpuInstance.InfoRecord.IA32HealthFlags.Uint32  = SecPlatformInformation->IA32HealthFlags.Uint32;
   1614       CpuInstance = &BspCpuInstance;
   1615     } else {
   1616       DEBUG ((EFI_D_INFO, "Does not find any HOB stored CPU BIST information!\n"));
   1617       //
   1618       // Does not find any HOB stored BIST information
   1619       //
   1620       return;
   1621     }
   1622   }
   1623 
   1624   while ((NumberOfData--) > 0) {
   1625     for (ProcessorNumber = 0; ProcessorNumber < mMpSystemData.NumberOfProcessors; ProcessorNumber++) {
   1626       CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
   1627       InitialLocalApicId = (UINT32) CpuData->Info.ProcessorId;
   1628       if (InitialLocalApicId == CpuInstance[NumberOfData].CpuLocation) {
   1629         //
   1630         // Update CPU health status for MP Services Protocol according to BIST data.
   1631         //
   1632         if (CpuInstance[NumberOfData].InfoRecord.IA32HealthFlags.Uint32 != 0) {
   1633           CpuData->Info.StatusFlag &= ~PROCESSOR_HEALTH_STATUS_BIT;
   1634           //
   1635           // Report Status Code that self test is failed
   1636           //
   1637           REPORT_STATUS_CODE (
   1638             EFI_ERROR_CODE | EFI_ERROR_MAJOR,
   1639             (EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_EC_SELF_TEST)
   1640             );
   1641         }
   1642       }
   1643     }
   1644   }
   1645 }
   1646 
   1647 /**
   1648   Callback function for ExitBootServices.
   1649 
   1650   @param  Event                 Event whose notification function is being invoked.
   1651   @param  Context               The pointer to the notification function's context,
   1652                                 which is implementation-dependent.
   1653 
   1654 **/
   1655 VOID
   1656 EFIAPI
   1657 ExitBootServicesCallback (
   1658   IN EFI_EVENT                Event,
   1659   IN VOID                     *Context
   1660   )
   1661 {
   1662   //
   1663   // Avoid APs access invalid buff datas which allocated by BootServices,
   1664   // so we send INIT IPI to APs to let them wait for SIPI state.
   1665   //
   1666   SendInitIpiAllExcludingSelf ();
   1667 }
   1668 
   1669 /**
   1670   A minimal wrapper function that allows MtrrSetAllMtrrs() to be passed to
   1671   EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() as Procedure.
   1672 
   1673   @param[in] Buffer  Pointer to an MTRR_SETTINGS object, to be passed to
   1674                      MtrrSetAllMtrrs().
   1675 **/
   1676 VOID
   1677 EFIAPI
   1678 SetMtrrsFromBuffer (
   1679   IN VOID *Buffer
   1680   )
   1681 {
   1682   MtrrSetAllMtrrs (Buffer);
   1683 }
   1684 
   1685 /**
   1686   Initialize Multi-processor support.
   1687 
   1688 **/
   1689 VOID
   1690 InitializeMpSupport (
   1691   VOID
   1692   )
   1693 {
   1694   EFI_STATUS     Status;
   1695   MTRR_SETTINGS  MtrrSettings;
   1696   UINTN          Timeout;
   1697 
   1698   gMaxLogicalProcessorNumber = (UINTN) PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
   1699   if (gMaxLogicalProcessorNumber < 1) {
   1700     DEBUG ((DEBUG_ERROR, "Setting PcdCpuMaxLogicalProcessorNumber should be more than zero.\n"));
   1701     return;
   1702   }
   1703 
   1704 
   1705 
   1706   InitMpSystemData ();
   1707 
   1708   //
   1709   // Only perform AP detection if PcdCpuMaxLogicalProcessorNumber is greater than 1
   1710   //
   1711   if (gMaxLogicalProcessorNumber > 1) {
   1712 
   1713     gApStackSize = (UINTN) PcdGet32 (PcdCpuApStackSize);
   1714     ASSERT ((gApStackSize & (SIZE_4KB - 1)) == 0);
   1715 
   1716     mApStackStart = AllocatePages (EFI_SIZE_TO_PAGES (gMaxLogicalProcessorNumber * gApStackSize));
   1717     ASSERT (mApStackStart != NULL);
   1718 
   1719     //
   1720     // the first buffer of stack size used for common stack, when the amount of AP
   1721     // more than 1, we should never free the common stack which maybe used for AP reset.
   1722     //
   1723     mCommonStack = mApStackStart;
   1724     mTopOfApCommonStack = (UINT8*) mApStackStart + gApStackSize;
   1725     mApStackStart = mTopOfApCommonStack;
   1726 
   1727     PrepareAPStartupCode ();
   1728 
   1729     StartApsStackless ();
   1730   }
   1731 
   1732   DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mMpSystemData.NumberOfProcessors));
   1733   if (mMpSystemData.NumberOfProcessors == 1) {
   1734     FreeApStartupCode ();
   1735     if (mCommonStack != NULL) {
   1736       FreePages (mCommonStack, EFI_SIZE_TO_PAGES (gMaxLogicalProcessorNumber * gApStackSize));
   1737     }
   1738   }
   1739 
   1740   mMpSystemData.CpuDatas = ReallocatePool (
   1741                              sizeof (CPU_DATA_BLOCK) * gMaxLogicalProcessorNumber,
   1742                              sizeof (CPU_DATA_BLOCK) * mMpSystemData.NumberOfProcessors,
   1743                              mMpSystemData.CpuDatas);
   1744 
   1745   //
   1746   // Release all APs to complete initialization and enter idle loop
   1747   //
   1748   mAPsAlreadyInitFinished = TRUE;
   1749 
   1750   //
   1751   // Wait for all APs to enter idle loop.
   1752   //
   1753   Timeout = 0;
   1754   do {
   1755     if (CheckAllAPsSleeping ()) {
   1756       break;
   1757     }
   1758     MicroSecondDelay (gPollInterval);
   1759     Timeout += gPollInterval;
   1760   } while (Timeout <= PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds));
   1761   ASSERT (Timeout <= PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds));
   1762 
   1763   //
   1764   // Update CPU healthy information from Guided HOB
   1765   //
   1766   CollectBistDataFromHob ();
   1767 
   1768   //
   1769   // Synchronize MTRR settings to APs.
   1770   //
   1771   MtrrGetAllMtrrs (&MtrrSettings);
   1772   Status = mMpServicesTemplate.StartupAllAPs (
   1773                                  &mMpServicesTemplate, // This
   1774                                  SetMtrrsFromBuffer,   // Procedure
   1775                                  TRUE,                 // SingleThread
   1776                                  NULL,                 // WaitEvent
   1777                                  0,                    // TimeoutInMicrosecsond
   1778                                  &MtrrSettings,        // ProcedureArgument
   1779                                  NULL                  // FailedCpuList
   1780                                  );
   1781   ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_STARTED);
   1782 
   1783   Status = gBS->InstallMultipleProtocolInterfaces (
   1784                   &mMpServiceHandle,
   1785                   &gEfiMpServiceProtocolGuid,  &mMpServicesTemplate,
   1786                   NULL
   1787                   );
   1788   ASSERT_EFI_ERROR (Status);
   1789 
   1790   if (mMpSystemData.NumberOfProcessors > 1 && mMpSystemData.NumberOfProcessors < gMaxLogicalProcessorNumber) {
   1791     if (mApStackStart != NULL) {
   1792       FreePages (mApStackStart, EFI_SIZE_TO_PAGES (
   1793                                   (gMaxLogicalProcessorNumber - mMpSystemData.NumberOfProcessors) *
   1794                                   gApStackSize));
   1795     }
   1796   }
   1797 
   1798   Status = gBS->CreateEvent (
   1799                   EVT_SIGNAL_EXIT_BOOT_SERVICES,
   1800                   TPL_CALLBACK,
   1801                   ExitBootServicesCallback,
   1802                   NULL,
   1803                   &mExitBootServicesEvent
   1804                   );
   1805   ASSERT_EFI_ERROR (Status);
   1806 }
   1807