Home | History | Annotate | Download | only in MpServicesOnFrameworkMpServicesThunk
      1 /** @file
      2 Produces PI MP Services Protocol on top of Framework MP Services Protocol.
      3 
      4 Intel's Framework MP Services Protocol is replaced by EFI_MP_SERVICES_PROTOCOL in PI 1.1.
      5 This module produces PI MP Services Protocol on top of Framework MP Services Protocol.
      6 
      7 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
      8 This program and the accompanying materials
      9 are licensed and made available under the terms and conditions of the BSD License
     10 which accompanies this distribution.  The full text of the license may be found at
     11 http://opensource.org/licenses/bsd-license.php
     12 
     13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     15 Module Name:
     16 
     17 **/
     18 
     19 #include "MpServicesOnFrameworkMpServicesThunk.h"
     20 
     21 EFI_HANDLE                          mHandle = NULL;
     22 MP_SYSTEM_DATA                      mMPSystemData;
     23 EFI_PHYSICAL_ADDRESS                mStartupVector;
     24 MP_CPU_EXCHANGE_INFO                *mExchangeInfo;
     25 BOOLEAN                             mStopCheckAPsStatus = FALSE;
     26 UINTN                               mNumberOfProcessors;
     27 EFI_GENERIC_MEMORY_TEST_PROTOCOL    *mGenMemoryTest;
     28 
     29 FRAMEWORK_EFI_MP_SERVICES_PROTOCOL  *mFrameworkMpService;
     30 EFI_MP_SERVICES_PROTOCOL            mMpService = {
     31   GetNumberOfProcessors,
     32   GetProcessorInfo,
     33   StartupAllAPs,
     34   StartupThisAP,
     35   SwitchBSP,
     36   EnableDisableAP,
     37   WhoAmI
     38 };
     39 
     40 
     41 /**
     42   Implementation of GetNumberOfProcessors() service of MP Services Protocol.
     43 
     44   This service retrieves the number of logical processor in the platform
     45   and the number of those logical processors that are enabled on this boot.
     46   This service may only be called from the BSP.
     47 
     48   @param  This                       A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
     49   @param  NumberOfProcessors         Pointer to the total number of logical processors in the system,
     50                                      including the BSP and disabled APs.
     51   @param  NumberOfEnabledProcessors  Pointer to the number of enabled logical processors that exist
     52                                      in system, including the BSP.
     53 
     54   @retval EFI_SUCCESS	             Number of logical processors and enabled logical processors retrieved..
     55   @retval EFI_DEVICE_ERROR           Caller processor is AP.
     56   @retval EFI_INVALID_PARAMETER      NumberOfProcessors is NULL
     57   @retval EFI_INVALID_PARAMETER      NumberOfEnabledProcessors is NULL
     58 
     59 **/
     60 EFI_STATUS
     61 EFIAPI
     62 GetNumberOfProcessors (
     63   IN  EFI_MP_SERVICES_PROTOCOL     *This,
     64   OUT UINTN                        *NumberOfProcessors,
     65   OUT UINTN                        *NumberOfEnabledProcessors
     66   )
     67 {
     68   EFI_STATUS      Status;
     69   UINTN           CallerNumber;
     70 
     71   //
     72   // Check whether caller processor is BSP
     73   //
     74   WhoAmI (This, &CallerNumber);
     75   if (CallerNumber != GetBspNumber ()) {
     76     return EFI_DEVICE_ERROR;
     77   }
     78 
     79   //
     80   // Check parameter NumberOfProcessors
     81   //
     82   if (NumberOfProcessors == NULL) {
     83     return EFI_INVALID_PARAMETER;
     84   }
     85 
     86   //
     87   // Check parameter NumberOfEnabledProcessors
     88   //
     89   if (NumberOfEnabledProcessors == NULL) {
     90     return EFI_INVALID_PARAMETER;
     91   }
     92 
     93   Status = mFrameworkMpService->GetGeneralMPInfo (
     94                                   mFrameworkMpService,
     95                                   NumberOfProcessors,
     96                                   NULL,
     97                                   NumberOfEnabledProcessors,
     98                                   NULL,
     99                                   NULL
    100                                   );
    101   ASSERT_EFI_ERROR (Status);
    102 
    103   return EFI_SUCCESS;
    104 }
    105 
    106 /**
    107   Implementation of GetNumberOfProcessors() service of MP Services Protocol.
    108 
    109   Gets detailed MP-related information on the requested processor at the
    110   instant this call is made. This service may only be called from the BSP.
    111 
    112   @param  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
    113   @param  ProcessorNumber       The handle number of processor.
    114   @param  ProcessorInfoBuffer   A pointer to the buffer where information for the requested processor is deposited.
    115 
    116   @retval EFI_SUCCESS           Processor information successfully returned.
    117   @retval EFI_DEVICE_ERROR      Caller processor is AP.
    118   @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL
    119   @retval EFI_NOT_FOUND         Processor with the handle specified by ProcessorNumber does not exist.
    120 
    121 **/
    122 EFI_STATUS
    123 EFIAPI
    124 GetProcessorInfo (
    125   IN       EFI_MP_SERVICES_PROTOCOL     *This,
    126   IN       UINTN                        ProcessorNumber,
    127   OUT      EFI_PROCESSOR_INFORMATION    *ProcessorInfoBuffer
    128   )
    129 {
    130   EFI_STATUS                 Status;
    131   UINTN                      CallerNumber;
    132   UINTN                      BufferSize;
    133   EFI_MP_PROC_CONTEXT        ProcessorContextBuffer;
    134 
    135   //
    136   // Check whether caller processor is BSP
    137   //
    138   WhoAmI (This, &CallerNumber);
    139   if (CallerNumber != GetBspNumber ()) {
    140     return EFI_DEVICE_ERROR;
    141   }
    142 
    143   //
    144   // Check parameter ProcessorInfoBuffer
    145   //
    146   if (ProcessorInfoBuffer == NULL) {
    147     return EFI_INVALID_PARAMETER;
    148   }
    149 
    150   //
    151   // Check whether processor with the handle specified by ProcessorNumber exists
    152   //
    153   if (ProcessorNumber >= mNumberOfProcessors) {
    154     return EFI_NOT_FOUND;
    155   }
    156 
    157   BufferSize = sizeof (EFI_MP_PROC_CONTEXT);
    158   Status = mFrameworkMpService->GetProcessorContext (
    159                                   mFrameworkMpService,
    160                                   ProcessorNumber,
    161                                   &BufferSize,
    162                                   &ProcessorContextBuffer
    163                                   );
    164   ASSERT_EFI_ERROR (Status);
    165 
    166   ProcessorInfoBuffer->ProcessorId = (UINT64) ProcessorContextBuffer.ApicID;
    167 
    168   //
    169   // Get Status Flag of specified processor
    170   //
    171   ProcessorInfoBuffer->StatusFlag = 0;
    172 
    173   if (ProcessorContextBuffer.Enabled) {
    174     ProcessorInfoBuffer->StatusFlag |= PROCESSOR_ENABLED_BIT;
    175   }
    176 
    177   if (ProcessorContextBuffer.Designation == EfiCpuBSP) {
    178     ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT;
    179   }
    180 
    181   if (ProcessorContextBuffer.Health.Flags.Uint32 == 0) {
    182     ProcessorInfoBuffer->StatusFlag |= PROCESSOR_HEALTH_STATUS_BIT;
    183   }
    184 
    185   ProcessorInfoBuffer->Location.Package = (UINT32) ProcessorContextBuffer.PackageNumber;
    186   ProcessorInfoBuffer->Location.Core    = (UINT32) ProcessorContextBuffer.NumberOfCores;
    187   ProcessorInfoBuffer->Location.Thread  = (UINT32) ProcessorContextBuffer.NumberOfThreads;
    188 
    189   return EFI_SUCCESS;
    190 }
    191 
    192 /**
    193   Implementation of StartupAllAPs() service of MP Services Protocol.
    194 
    195   This service lets the caller get all enabled APs to execute a caller-provided function.
    196   This service may only be called from the BSP.
    197 
    198   @param  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
    199   @param  Procedure             A pointer to the function to be run on enabled APs of the system.
    200   @param  SingleThread          Indicates whether to execute the function simultaneously or one by one..
    201   @param  WaitEvent             The event created by the caller.
    202                                 If it is NULL, then execute in blocking mode.
    203                                 If it is not NULL, then execute in non-blocking mode.
    204   @param  TimeoutInMicroSeconds The time limit in microseconds for this AP to finish the function.
    205                                 Zero means infinity.
    206   @param  ProcedureArgument     Pointer to the optional parameter of the assigned function.
    207   @param  FailedCpuList         The list of processor numbers that fail to finish the function before
    208                                 TimeoutInMicrosecsond expires.
    209 
    210   @retval EFI_SUCCESS           In blocking mode, all APs have finished before the timeout expired.
    211   @retval EFI_SUCCESS           In non-blocking mode, function has been dispatched to all enabled APs.
    212   @retval EFI_DEVICE_ERROR      Caller processor is AP.
    213   @retval EFI_NOT_STARTED       No enabled AP exists in the system.
    214   @retval EFI_NOT_READY         Any enabled AP is busy.
    215   @retval EFI_TIMEOUT           In blocking mode, The timeout expired before all enabled APs have finished.
    216   @retval EFI_INVALID_PARAMETER Procedure is NULL.
    217 
    218 **/
    219 EFI_STATUS
    220 EFIAPI
    221 StartupAllAPs (
    222   IN  EFI_MP_SERVICES_PROTOCOL   *This,
    223   IN  EFI_AP_PROCEDURE           Procedure,
    224   IN  BOOLEAN                    SingleThread,
    225   IN  EFI_EVENT                  WaitEvent             OPTIONAL,
    226   IN  UINTN                      TimeoutInMicroSeconds,
    227   IN  VOID                       *ProcedureArgument    OPTIONAL,
    228   OUT UINTN                      **FailedCpuList       OPTIONAL
    229   )
    230 {
    231   EFI_STATUS      Status;
    232   UINTN           ProcessorNumber;
    233   CPU_DATA_BLOCK  *CpuData;
    234   BOOLEAN         Blocking;
    235   UINTN           BspNumber;
    236 
    237   if (FailedCpuList != NULL) {
    238     *FailedCpuList = NULL;
    239   }
    240 
    241   //
    242   // Check whether caller processor is BSP
    243   //
    244   BspNumber = GetBspNumber ();
    245   WhoAmI (This, &ProcessorNumber);
    246   if (ProcessorNumber != BspNumber) {
    247     return EFI_DEVICE_ERROR;
    248   }
    249 
    250   //
    251   // Check parameter Procedure
    252   //
    253   if (Procedure == NULL) {
    254     return EFI_INVALID_PARAMETER;
    255   }
    256 
    257   //
    258   // Temporarily suppress CheckAPsStatus()
    259   //
    260   mStopCheckAPsStatus = TRUE;
    261 
    262   //
    263   // Check whether all enabled APs are idle.
    264   // If any enabled AP is not idle, return EFI_NOT_READY.
    265   //
    266   for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
    267 
    268     CpuData = &mMPSystemData.CpuData[ProcessorNumber];
    269 
    270     mMPSystemData.CpuList[ProcessorNumber] = FALSE;
    271     if (ProcessorNumber != BspNumber) {
    272       if (CpuData->State != CpuStateDisabled) {
    273         if (CpuData->State != CpuStateIdle) {
    274           mStopCheckAPsStatus = FALSE;
    275           return EFI_NOT_READY;
    276         } else {
    277           //
    278           // Mark this processor as responsible for current calling.
    279           //
    280           mMPSystemData.CpuList[ProcessorNumber] = TRUE;
    281         }
    282       }
    283     }
    284   }
    285 
    286   mMPSystemData.FinishCount = 0;
    287   mMPSystemData.StartCount  = 0;
    288   Blocking                  = FALSE;
    289   //
    290   // Go through all enabled APs to wakeup them for Procedure.
    291   // If in Single Thread mode, then only one AP is woken up, and others are waiting.
    292   //
    293   for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
    294 
    295     CpuData = &mMPSystemData.CpuData[ProcessorNumber];
    296     //
    297     // Check whether this processor is responsible for current calling.
    298     //
    299     if (mMPSystemData.CpuList[ProcessorNumber]) {
    300 
    301       mMPSystemData.StartCount++;
    302 
    303       AcquireSpinLock (&CpuData->CpuDataLock);
    304       CpuData->State = CpuStateReady;
    305       ReleaseSpinLock (&CpuData->CpuDataLock);
    306 
    307       if (!Blocking) {
    308         WakeUpAp (
    309           ProcessorNumber,
    310           Procedure,
    311           ProcedureArgument
    312           );
    313       }
    314 
    315       if (SingleThread) {
    316         Blocking = TRUE;
    317       }
    318     }
    319   }
    320 
    321   //
    322   // If no enabled AP exists, return EFI_NOT_STARTED.
    323   //
    324   if (mMPSystemData.StartCount == 0) {
    325     mStopCheckAPsStatus = FALSE;
    326     return EFI_NOT_STARTED;
    327   }
    328 
    329   //
    330   // If WaitEvent is not NULL, execute in non-blocking mode.
    331   // BSP saves data for CheckAPsStatus(), and returns EFI_SUCCESS.
    332   // CheckAPsStatus() will check completion and timeout periodically.
    333   //
    334   mMPSystemData.Procedure      = Procedure;
    335   mMPSystemData.ProcArguments  = ProcedureArgument;
    336   mMPSystemData.SingleThread   = SingleThread;
    337   mMPSystemData.FailedCpuList  = FailedCpuList;
    338   mMPSystemData.ExpectedTime   = CalculateTimeout (TimeoutInMicroSeconds, &mMPSystemData.CurrentTime);
    339   mMPSystemData.WaitEvent      = WaitEvent;
    340 
    341   //
    342   // Allow CheckAPsStatus()
    343   //
    344   mStopCheckAPsStatus = FALSE;
    345 
    346   if (WaitEvent != NULL) {
    347     return EFI_SUCCESS;
    348   }
    349 
    350   //
    351   // If WaitEvent is NULL, execute in blocking mode.
    352   // BSP checks APs'state until all APs finish or TimeoutInMicrosecsond expires.
    353   //
    354   do {
    355     Status = CheckAllAPs ();
    356   } while (Status == EFI_NOT_READY);
    357 
    358   return Status;
    359 }
    360 
    361 /**
    362   Implementation of StartupThisAP() service of MP Services Protocol.
    363 
    364   This service lets the caller get one enabled AP to execute a caller-provided function.
    365   This service may only be called from the BSP.
    366 
    367   @param  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
    368   @param  Procedure             A pointer to the function to be run on the designated AP.
    369   @param  ProcessorNumber       The handle number of AP..
    370   @param  WaitEvent             The event created by the caller.
    371                                 If it is NULL, then execute in blocking mode.
    372                                 If it is not NULL, then execute in non-blocking mode.
    373   @param  TimeoutInMicroseconds The time limit in microseconds for this AP to finish the function.
    374                                 Zero means infinity.
    375   @param  ProcedureArgument     Pointer to the optional parameter of the assigned function.
    376   @param  Finished              Indicates whether AP has finished assigned function.
    377                                 In blocking mode, it is ignored.
    378 
    379   @retval EFI_SUCCESS           In blocking mode, specified AP has finished before the timeout expires.
    380   @retval EFI_SUCCESS           In non-blocking mode, function has been dispatched to specified AP.
    381   @retval EFI_DEVICE_ERROR      Caller processor is AP.
    382   @retval EFI_TIMEOUT           In blocking mode, the timeout expires before specified AP has finished.
    383   @retval EFI_NOT_READY         Specified AP is busy.
    384   @retval EFI_NOT_FOUND         Processor with the handle specified by ProcessorNumber does not exist.
    385   @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.
    386   @retval EFI_INVALID_PARAMETER Procedure is NULL.
    387 
    388 **/
    389 EFI_STATUS
    390 EFIAPI
    391 StartupThisAP (
    392   IN  EFI_MP_SERVICES_PROTOCOL   *This,
    393   IN  EFI_AP_PROCEDURE           Procedure,
    394   IN  UINTN                      ProcessorNumber,
    395   IN  EFI_EVENT                  WaitEvent OPTIONAL,
    396   IN  UINTN                      TimeoutInMicroseconds,
    397   IN  VOID                       *ProcedureArgument OPTIONAL,
    398   OUT BOOLEAN                    *Finished OPTIONAL
    399   )
    400 {
    401   CPU_DATA_BLOCK  *CpuData;
    402   UINTN           CallerNumber;
    403   EFI_STATUS      Status;
    404   UINTN           BspNumber;
    405 
    406   if (Finished != NULL) {
    407     *Finished = TRUE;
    408   }
    409 
    410   //
    411   // Check whether caller processor is BSP
    412   //
    413   BspNumber = GetBspNumber ();
    414   WhoAmI (This, &CallerNumber);
    415   if (CallerNumber != BspNumber) {
    416     return EFI_DEVICE_ERROR;
    417   }
    418 
    419   //
    420   // Check whether processor with the handle specified by ProcessorNumber exists
    421   //
    422   if (ProcessorNumber >= mNumberOfProcessors) {
    423     return EFI_NOT_FOUND;
    424   }
    425 
    426   //
    427   // Check whether specified processor is BSP
    428   //
    429   if (ProcessorNumber == BspNumber) {
    430     return EFI_INVALID_PARAMETER;
    431   }
    432 
    433   //
    434   // Check parameter Procedure
    435   //
    436   if (Procedure == NULL) {
    437     return EFI_INVALID_PARAMETER;
    438   }
    439 
    440   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
    441 
    442   //
    443   // Temporarily suppress CheckAPsStatus()
    444   //
    445   mStopCheckAPsStatus = TRUE;
    446 
    447   //
    448   // Check whether specified AP is disabled
    449   //
    450   if (CpuData->State == CpuStateDisabled) {
    451     mStopCheckAPsStatus = FALSE;
    452     return EFI_INVALID_PARAMETER;
    453   }
    454 
    455   //
    456   // Check whether specified AP is busy
    457   //
    458   if (CpuData->State != CpuStateIdle) {
    459     mStopCheckAPsStatus = FALSE;
    460     return EFI_NOT_READY;
    461   }
    462 
    463   //
    464   // Wakeup specified AP for Procedure.
    465   //
    466   AcquireSpinLock (&CpuData->CpuDataLock);
    467   CpuData->State = CpuStateReady;
    468   ReleaseSpinLock (&CpuData->CpuDataLock);
    469 
    470   WakeUpAp (
    471     ProcessorNumber,
    472     Procedure,
    473     ProcedureArgument
    474     );
    475 
    476   //
    477   // If WaitEvent is not NULL, execute in non-blocking mode.
    478   // BSP saves data for CheckAPsStatus(), and returns EFI_SUCCESS.
    479   // CheckAPsStatus() will check completion and timeout periodically.
    480   //
    481   CpuData->WaitEvent      = WaitEvent;
    482   CpuData->Finished       = Finished;
    483   CpuData->ExpectedTime   = CalculateTimeout (TimeoutInMicroseconds, &CpuData->CurrentTime);
    484 
    485   //
    486   // Allow CheckAPsStatus()
    487   //
    488   mStopCheckAPsStatus = FALSE;
    489 
    490   if (WaitEvent != NULL) {
    491     return EFI_SUCCESS;
    492   }
    493 
    494   //
    495   // If WaitEvent is NULL, execute in blocking mode.
    496   // BSP checks AP's state until it finishes or TimeoutInMicrosecsond expires.
    497   //
    498   do {
    499     Status = CheckThisAP (ProcessorNumber);
    500   } while (Status == EFI_NOT_READY);
    501 
    502   return Status;
    503 }
    504 
    505 /**
    506   Implementation of SwitchBSP() service of MP Services Protocol.
    507 
    508   This service switches the requested AP to be the BSP from that point onward.
    509   This service may only be called from the current BSP.
    510 
    511   @param  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
    512   @param  ProcessorNumber       The handle number of processor.
    513   @param  EnableOldBSP          Whether to enable or disable the original BSP.
    514 
    515   @retval EFI_SUCCESS           BSP successfully switched.
    516   @retval EFI_DEVICE_ERROR      Caller processor is AP.
    517   @retval EFI_NOT_FOUND         Processor with the handle specified by ProcessorNumber does not exist.
    518   @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.
    519   @retval EFI_NOT_READY         Specified AP is busy.
    520 
    521 **/
    522 EFI_STATUS
    523 EFIAPI
    524 SwitchBSP (
    525   IN  EFI_MP_SERVICES_PROTOCOL            *This,
    526   IN  UINTN                               ProcessorNumber,
    527   IN  BOOLEAN                             EnableOldBSP
    528   )
    529 {
    530   EFI_STATUS            Status;
    531   CPU_DATA_BLOCK        *CpuData;
    532   UINTN                 CallerNumber;
    533   UINTN                 BspNumber;
    534   UINTN                 ApicBase;
    535   UINT32                CurrentTimerValue;
    536   UINT32                CurrentTimerRegister;
    537   UINT32                CurrentTimerDivide;
    538   UINT64                CurrentTscValue;
    539   BOOLEAN               OldInterruptState;
    540 
    541   //
    542   // Check whether caller processor is BSP
    543   //
    544   BspNumber = GetBspNumber ();
    545   WhoAmI (This, &CallerNumber);
    546   if (CallerNumber != BspNumber) {
    547     return EFI_DEVICE_ERROR;
    548   }
    549 
    550   //
    551   // Check whether processor with the handle specified by ProcessorNumber exists
    552   //
    553   if (ProcessorNumber >= mNumberOfProcessors) {
    554     return EFI_NOT_FOUND;
    555   }
    556 
    557   //
    558   // Check whether specified processor is BSP
    559   //
    560   if (ProcessorNumber == BspNumber) {
    561     return EFI_INVALID_PARAMETER;
    562   }
    563 
    564   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
    565 
    566   //
    567   // Check whether specified AP is disabled
    568   //
    569   if (CpuData->State == CpuStateDisabled) {
    570     return EFI_INVALID_PARAMETER;
    571   }
    572 
    573   //
    574   // Check whether specified AP is busy
    575   //
    576   if (CpuData->State != CpuStateIdle) {
    577     return EFI_NOT_READY;
    578   }
    579 
    580   //
    581   // Save and disable interrupt.
    582   //
    583   OldInterruptState = SaveAndDisableInterrupts ();
    584 
    585   //
    586   // Record the current local APIC timer setting of BSP
    587   //
    588   ApicBase = (UINTN)AsmMsrBitFieldRead64 (MSR_IA32_APIC_BASE, 12, 35) << 12;
    589   CurrentTimerValue     = MmioRead32 (ApicBase + APIC_REGISTER_TIMER_COUNT);
    590   CurrentTimerRegister  = MmioRead32 (ApicBase + APIC_REGISTER_LVT_TIMER);
    591   CurrentTimerDivide    = MmioRead32 (ApicBase + APIC_REGISTER_TIMER_DIVIDE);
    592   //
    593   // Set mask bit (BIT 16) of LVT Timer Register to disable its interrupt
    594   //
    595   MmioBitFieldWrite32 (ApicBase + APIC_REGISTER_LVT_TIMER, 16, 16, 1);
    596 
    597   //
    598   // Record the current TSC value
    599   //
    600   CurrentTscValue = AsmReadTsc ();
    601 
    602   Status = mFrameworkMpService->SwitchBSP (
    603                                   mFrameworkMpService,
    604                                   ProcessorNumber,
    605                                   EnableOldBSP
    606                                   );
    607   ASSERT_EFI_ERROR (Status);
    608 
    609   //
    610   // Restore TSC value
    611   //
    612   AsmWriteMsr64 (MSR_IA32_TIME_STAMP_COUNTER, CurrentTscValue);
    613 
    614   //
    615   // Restore local APIC timer setting to new BSP
    616   //
    617   MmioWrite32 (ApicBase + APIC_REGISTER_TIMER_DIVIDE, CurrentTimerDivide);
    618   MmioWrite32 (ApicBase + APIC_REGISTER_TIMER_INIT_COUNT, CurrentTimerValue);
    619   MmioWrite32 (ApicBase + APIC_REGISTER_LVT_TIMER, CurrentTimerRegister);
    620 
    621   //
    622   // Restore interrupt state.
    623   //
    624   SetInterruptState (OldInterruptState);
    625 
    626   ChangeCpuState (BspNumber, EnableOldBSP);
    627 
    628   return EFI_SUCCESS;
    629 }
    630 
    631 /**
    632   Implementation of EnableDisableAP() service of MP Services Protocol.
    633 
    634   This service lets the caller enable or disable an AP.
    635   This service may only be called from the BSP.
    636 
    637   @param  This                   A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
    638   @param  ProcessorNumber        The handle number of processor.
    639   @param  EnableAP               Indicates whether the newstate of the AP is enabled or disabled.
    640   @param  HealthFlag             Indicates new health state of the AP..
    641 
    642   @retval EFI_SUCCESS            AP successfully enabled or disabled.
    643   @retval EFI_DEVICE_ERROR       Caller processor is AP.
    644   @retval EFI_NOT_FOUND          Processor with the handle specified by ProcessorNumber does not exist.
    645   @retval EFI_INVALID_PARAMETERS ProcessorNumber specifies the BSP.
    646 
    647 **/
    648 EFI_STATUS
    649 EFIAPI
    650 EnableDisableAP (
    651   IN  EFI_MP_SERVICES_PROTOCOL            *This,
    652   IN  UINTN                               ProcessorNumber,
    653   IN  BOOLEAN                             EnableAP,
    654   IN  UINT32                              *HealthFlag OPTIONAL
    655   )
    656 {
    657   EFI_STATUS      Status;
    658   UINTN           CallerNumber;
    659   EFI_MP_HEALTH   HealthState;
    660   EFI_MP_HEALTH   *HealthStatePointer;
    661   UINTN           BspNumber;
    662 
    663   //
    664   // Check whether caller processor is BSP
    665   //
    666   BspNumber = GetBspNumber ();
    667   WhoAmI (This, &CallerNumber);
    668   if (CallerNumber != BspNumber) {
    669     return EFI_DEVICE_ERROR;
    670   }
    671 
    672   //
    673   // Check whether processor with the handle specified by ProcessorNumber exists
    674   //
    675   if (ProcessorNumber >= mNumberOfProcessors) {
    676     return EFI_NOT_FOUND;
    677   }
    678 
    679   //
    680   // Check whether specified processor is BSP
    681   //
    682   if (ProcessorNumber == BspNumber) {
    683     return EFI_INVALID_PARAMETER;
    684   }
    685 
    686   if (HealthFlag == NULL) {
    687     HealthStatePointer = NULL;
    688   } else {
    689     if ((*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT) == 0) {
    690       HealthState.Flags.Uint32 = 1;
    691     } else {
    692       HealthState.Flags.Uint32 = 0;
    693     }
    694     HealthState.TestStatus = 0;
    695 
    696     HealthStatePointer = &HealthState;
    697   }
    698 
    699   Status = mFrameworkMpService->EnableDisableAP (
    700                                   mFrameworkMpService,
    701                                   ProcessorNumber,
    702                                   EnableAP,
    703                                   HealthStatePointer
    704                                   );
    705   ASSERT_EFI_ERROR (Status);
    706 
    707   ChangeCpuState (ProcessorNumber, EnableAP);
    708 
    709   return EFI_SUCCESS;
    710 }
    711 
    712 /**
    713   Implementation of WhoAmI() service of MP Services Protocol.
    714 
    715   This service lets the caller processor get its handle number.
    716   This service may be called from the BSP and APs.
    717 
    718   @param  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
    719   @param  ProcessorNumber       Pointer to the handle number of AP.
    720 
    721   @retval EFI_SUCCESS           Processor number successfully returned.
    722   @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL
    723 
    724 **/
    725 EFI_STATUS
    726 EFIAPI
    727 WhoAmI (
    728   IN  EFI_MP_SERVICES_PROTOCOL            *This,
    729   OUT UINTN                               *ProcessorNumber
    730   )
    731 {
    732   EFI_STATUS  Status;
    733 
    734   if (ProcessorNumber == NULL) {
    735     return EFI_INVALID_PARAMETER;
    736   }
    737 
    738   Status = mFrameworkMpService->WhoAmI (
    739                                   mFrameworkMpService,
    740                                   ProcessorNumber
    741                                   );
    742   ASSERT_EFI_ERROR (Status);
    743 
    744   return EFI_SUCCESS;
    745 }
    746 
    747 /**
    748   Checks APs' status periodically.
    749 
    750   This function is triggerred by timer perodically to check the
    751   state of APs for StartupAllAPs() and StartupThisAP() executed
    752   in non-blocking mode.
    753 
    754   @param  Event    Event triggered.
    755   @param  Context  Parameter passed with the event.
    756 
    757 **/
    758 VOID
    759 EFIAPI
    760 CheckAPsStatus (
    761   IN  EFI_EVENT                           Event,
    762   IN  VOID                                *Context
    763   )
    764 {
    765   UINTN           ProcessorNumber;
    766   CPU_DATA_BLOCK  *CpuData;
    767   EFI_STATUS      Status;
    768 
    769   //
    770   // If CheckAPsStatus() is stopped, then return immediately.
    771   //
    772   if (mStopCheckAPsStatus) {
    773     return;
    774   }
    775 
    776   //
    777   // First, check whether pending StartupAllAPs() exists.
    778   //
    779   if (mMPSystemData.WaitEvent != NULL) {
    780 
    781     Status = CheckAllAPs ();
    782     //
    783     // If all APs finish for StartupAllAPs(), signal the WaitEvent for it..
    784     //
    785     if (Status != EFI_NOT_READY) {
    786       Status = gBS->SignalEvent (mMPSystemData.WaitEvent);
    787       mMPSystemData.WaitEvent = NULL;
    788     }
    789   }
    790 
    791   //
    792   // Second, check whether pending StartupThisAPs() callings exist.
    793   //
    794   for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
    795 
    796     CpuData = &mMPSystemData.CpuData[ProcessorNumber];
    797 
    798     if (CpuData->WaitEvent == NULL) {
    799       continue;
    800     }
    801 
    802     Status = CheckThisAP (ProcessorNumber);
    803 
    804     if (Status != EFI_NOT_READY) {
    805       gBS->SignalEvent (CpuData->WaitEvent);
    806       CpuData->WaitEvent = NULL;
    807     }
    808   }
    809   return ;
    810 }
    811 
    812 /**
    813   Checks status of all APs.
    814 
    815   This function checks whether all APs have finished task assigned by StartupAllAPs(),
    816   and whether timeout expires.
    817 
    818   @retval EFI_SUCCESS           All APs have finished task assigned by StartupAllAPs().
    819   @retval EFI_TIMEOUT           The timeout expires.
    820   @retval EFI_NOT_READY         APs have not finished task and timeout has not expired.
    821 
    822 **/
    823 EFI_STATUS
    824 CheckAllAPs (
    825   VOID
    826   )
    827 {
    828   UINTN           ProcessorNumber;
    829   UINTN           NextProcessorNumber;
    830   UINTN           ListIndex;
    831   EFI_STATUS      Status;
    832   CPU_STATE       CpuState;
    833   CPU_DATA_BLOCK  *CpuData;
    834 
    835   NextProcessorNumber = 0;
    836 
    837   //
    838   // Go through all APs that are responsible for the StartupAllAPs().
    839   //
    840   for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
    841     if (!mMPSystemData.CpuList[ProcessorNumber]) {
    842       continue;
    843     }
    844 
    845     CpuData = &mMPSystemData.CpuData[ProcessorNumber];
    846 
    847     //
    848     //  Check the CPU state of AP. If it is CpuStateFinished, then the AP has finished its task.
    849     //  Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the
    850     //  value of state after setting the it to CpuStateFinished, so BSP can safely make use of its value.
    851     //
    852     AcquireSpinLock (&CpuData->CpuDataLock);
    853     CpuState = CpuData->State;
    854     ReleaseSpinLock (&CpuData->CpuDataLock);
    855 
    856     if (CpuState == CpuStateFinished) {
    857       mMPSystemData.FinishCount++;
    858       mMPSystemData.CpuList[ProcessorNumber] = FALSE;
    859 
    860       AcquireSpinLock (&CpuData->CpuDataLock);
    861       CpuData->State = CpuStateIdle;
    862       ReleaseSpinLock (&CpuData->CpuDataLock);
    863 
    864       //
    865       // If in Single Thread mode, then search for the next waiting AP for execution.
    866       //
    867       if (mMPSystemData.SingleThread) {
    868         Status = GetNextWaitingProcessorNumber (&NextProcessorNumber);
    869 
    870         if (!EFI_ERROR (Status)) {
    871           WakeUpAp (
    872             NextProcessorNumber,
    873             mMPSystemData.Procedure,
    874             mMPSystemData.ProcArguments
    875             );
    876         }
    877       }
    878     }
    879   }
    880 
    881   //
    882   // If all APs finish, return EFI_SUCCESS.
    883   //
    884   if (mMPSystemData.FinishCount == mMPSystemData.StartCount) {
    885     return EFI_SUCCESS;
    886   }
    887 
    888   //
    889   // If timeout expires, report timeout.
    890   //
    891   if (CheckTimeout (&mMPSystemData.CurrentTime, &mMPSystemData.TotalTime, mMPSystemData.ExpectedTime)) {
    892     //
    893     // If FailedCpuList is not NULL, record all failed APs in it.
    894     //
    895     if (mMPSystemData.FailedCpuList != NULL) {
    896       *mMPSystemData.FailedCpuList = AllocatePool ((mMPSystemData.StartCount - mMPSystemData.FinishCount + 1) * sizeof(UINTN));
    897       ASSERT (*mMPSystemData.FailedCpuList != NULL);
    898     }
    899     ListIndex = 0;
    900 
    901     for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
    902       //
    903       // Check whether this processor is responsible for StartupAllAPs().
    904       //
    905       if (mMPSystemData.CpuList[ProcessorNumber]) {
    906         //
    907         // Reset failed APs to idle state
    908         //
    909         ResetProcessorToIdleState (ProcessorNumber);
    910         mMPSystemData.CpuList[ProcessorNumber] = FALSE;
    911         if (mMPSystemData.FailedCpuList != NULL) {
    912           (*mMPSystemData.FailedCpuList)[ListIndex++] = ProcessorNumber;
    913         }
    914       }
    915     }
    916     if (mMPSystemData.FailedCpuList != NULL) {
    917       (*mMPSystemData.FailedCpuList)[ListIndex] = END_OF_CPU_LIST;
    918     }
    919     return EFI_TIMEOUT;
    920   }
    921   return EFI_NOT_READY;
    922 }
    923 
    924 /**
    925   Checks status of specified AP.
    926 
    927   This function checks whether specified AP has finished task assigned by StartupThisAP(),
    928   and whether timeout expires.
    929 
    930   @param  ProcessorNumber       The handle number of processor.
    931 
    932   @retval EFI_SUCCESS           Specified AP has finished task assigned by StartupThisAPs().
    933   @retval EFI_TIMEOUT           The timeout expires.
    934   @retval EFI_NOT_READY         Specified AP has not finished task and timeout has not expired.
    935 
    936 **/
    937 EFI_STATUS
    938 CheckThisAP (
    939   UINTN  ProcessorNumber
    940   )
    941 {
    942   CPU_DATA_BLOCK  *CpuData;
    943   CPU_STATE       CpuState;
    944 
    945   ASSERT (ProcessorNumber < mNumberOfProcessors);
    946   ASSERT (ProcessorNumber < MAX_CPU_NUMBER);
    947 
    948   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
    949 
    950   //
    951   //  Check the CPU state of AP. If it is CpuStateFinished, then the AP has finished its task.
    952   //  Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the
    953   //  value of state after setting the it to CpuStateFinished, so BSP can safely make use of its value.
    954   //
    955   AcquireSpinLock (&CpuData->CpuDataLock);
    956   CpuState = CpuData->State;
    957   ReleaseSpinLock (&CpuData->CpuDataLock);
    958 
    959   //
    960   // If the APs finishes for StartupThisAP(), return EFI_SUCCESS.
    961   //
    962   if (CpuState == CpuStateFinished) {
    963 
    964     AcquireSpinLock (&CpuData->CpuDataLock);
    965     CpuData->State = CpuStateIdle;
    966     ReleaseSpinLock (&CpuData->CpuDataLock);
    967 
    968     if (CpuData->Finished != NULL) {
    969       *(CpuData->Finished) = TRUE;
    970     }
    971     return EFI_SUCCESS;
    972   } else {
    973     //
    974     // If timeout expires for StartupThisAP(), report timeout.
    975     //
    976     if (CheckTimeout (&CpuData->CurrentTime, &CpuData->TotalTime, CpuData->ExpectedTime)) {
    977 
    978       if (CpuData->Finished != NULL) {
    979         *(CpuData->Finished) = FALSE;
    980       }
    981       //
    982       // Reset failed AP to idle state
    983       //
    984       ResetProcessorToIdleState (ProcessorNumber);
    985 
    986       return EFI_TIMEOUT;
    987     }
    988   }
    989   return EFI_NOT_READY;
    990 }
    991 
    992 /**
    993   Calculate timeout value and return the current performance counter value.
    994 
    995   Calculate the number of performance counter ticks required for a timeout.
    996   If TimeoutInMicroseconds is 0, return value is also 0, which is recognized
    997   as infinity.
    998 
    999   @param  TimeoutInMicroseconds   Timeout value in microseconds.
   1000   @param  CurrentTime             Returns the current value of the performance counter.
   1001 
   1002   @return Expected timestamp counter for timeout.
   1003           If TimeoutInMicroseconds is 0, return value is also 0, which is recognized
   1004           as infinity.
   1005 
   1006 **/
   1007 UINT64
   1008 CalculateTimeout (
   1009   IN  UINTN   TimeoutInMicroseconds,
   1010   OUT UINT64  *CurrentTime
   1011   )
   1012 {
   1013   //
   1014   // Read the current value of the performance counter
   1015   //
   1016   *CurrentTime = GetPerformanceCounter ();
   1017 
   1018   //
   1019   // If TimeoutInMicroseconds is 0, return value is also 0, which is recognized
   1020   // as infinity.
   1021   //
   1022   if (TimeoutInMicroseconds == 0) {
   1023     return 0;
   1024   }
   1025 
   1026   //
   1027   // GetPerformanceCounterProperties () returns the timestamp counter's frequency
   1028   // in Hz. So multiply the return value with TimeoutInMicroseconds and then divide
   1029   // it by 1,000,000, to get the number of ticks for the timeout value.
   1030   //
   1031   return DivU64x32 (
   1032            MultU64x64 (
   1033              GetPerformanceCounterProperties (NULL, NULL),
   1034              TimeoutInMicroseconds
   1035              ),
   1036            1000000
   1037            );
   1038 }
   1039 
   1040 /**
   1041   Checks whether timeout expires.
   1042 
   1043   Check whether the number of ellapsed performance counter ticks required for a timeout condition
   1044   has been reached.  If Timeout is zero, which means infinity, return value is always FALSE.
   1045 
   1046   @param  PreviousTime         On input, the value of the performance counter when it was last read.
   1047                                On output, the current value of the performance counter
   1048   @param  TotalTime            The total amount of ellapsed time in performance counter ticks.
   1049   @param  Timeout              The number of performance counter ticks required to reach a timeout condition.
   1050 
   1051   @retval TRUE                 A timeout condition has been reached.
   1052   @retval FALSE                A timeout condition has not been reached.
   1053 
   1054 **/
   1055 BOOLEAN
   1056 CheckTimeout (
   1057   IN OUT UINT64  *PreviousTime,
   1058   IN     UINT64  *TotalTime,
   1059   IN     UINT64  Timeout
   1060   )
   1061 {
   1062   UINT64  Start;
   1063   UINT64  End;
   1064   UINT64  CurrentTime;
   1065   INT64   Delta;
   1066   INT64   Cycle;
   1067 
   1068   if (Timeout == 0) {
   1069     return FALSE;
   1070   }
   1071   GetPerformanceCounterProperties (&Start, &End);
   1072   Cycle = End - Start;
   1073   if (Cycle < 0) {
   1074     Cycle = -Cycle;
   1075   }
   1076   Cycle++;
   1077   CurrentTime = GetPerformanceCounter();
   1078   Delta = (INT64) (CurrentTime - *PreviousTime);
   1079   if (Start > End) {
   1080     Delta = -Delta;
   1081   }
   1082   if (Delta < 0) {
   1083     Delta += Cycle;
   1084   }
   1085   *TotalTime += Delta;
   1086   *PreviousTime = CurrentTime;
   1087   if (*TotalTime > Timeout) {
   1088     return TRUE;
   1089   }
   1090   return FALSE;
   1091 }
   1092 
   1093 /**
   1094   Searches for the next waiting AP.
   1095 
   1096   Search for the next AP that is put in waiting state by single-threaded StartupAllAPs().
   1097 
   1098   @param  NextProcessorNumber  Pointer to the processor number of the next waiting AP.
   1099 
   1100   @retval EFI_SUCCESS          The next waiting AP has been found.
   1101   @retval EFI_NOT_FOUND        No waiting AP exists.
   1102 
   1103 **/
   1104 EFI_STATUS
   1105 GetNextWaitingProcessorNumber (
   1106   OUT UINTN                               *NextProcessorNumber
   1107   )
   1108 {
   1109   UINTN           ProcessorNumber;
   1110 
   1111   for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
   1112 
   1113     if (mMPSystemData.CpuList[ProcessorNumber]) {
   1114       *NextProcessorNumber = ProcessorNumber;
   1115       return EFI_SUCCESS;
   1116     }
   1117   }
   1118 
   1119   return EFI_NOT_FOUND;
   1120 }
   1121 
   1122 
   1123 /**
   1124   Wrapper function for all procedures assigned to AP.
   1125 
   1126   Wrapper function for all procedures assigned to AP via MP service protocol.
   1127   It controls states of AP and invokes assigned precedure.
   1128 
   1129 **/
   1130 VOID
   1131 ApProcWrapper (
   1132   VOID
   1133   )
   1134 {
   1135   EFI_AP_PROCEDURE      Procedure;
   1136   VOID                  *Parameter;
   1137   UINTN                 ProcessorNumber;
   1138   CPU_DATA_BLOCK        *CpuData;
   1139 
   1140   //
   1141   // Program virtual wire mode for AP, since it will be lost after AP wake up
   1142   //
   1143   ProgramVirtualWireMode ();
   1144   DisableLvtInterrupts ();
   1145 
   1146   //
   1147   // Initialize Debug Agent to support source level debug on AP code.
   1148   //
   1149   InitializeDebugAgent (DEBUG_AGENT_INIT_DXE_AP, NULL, NULL);
   1150 
   1151   WhoAmI (&mMpService, &ProcessorNumber);
   1152   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
   1153 
   1154   AcquireSpinLock (&CpuData->CpuDataLock);
   1155   CpuData->State = CpuStateBusy;
   1156   ReleaseSpinLock (&CpuData->CpuDataLock);
   1157 
   1158   //
   1159   // Now let us check it out.
   1160   //
   1161   AcquireSpinLock (&CpuData->CpuDataLock);
   1162   Procedure = CpuData->Procedure;
   1163   Parameter = CpuData->Parameter;
   1164   ReleaseSpinLock (&CpuData->CpuDataLock);
   1165 
   1166   if (Procedure != NULL) {
   1167 
   1168     Procedure (Parameter);
   1169 
   1170     //
   1171     // if BSP is switched to AP, it continue execute from here, but it carries register state
   1172     // of the old AP, so need to reload CpuData (might be stored in a register after compiler
   1173     // optimization) to make sure it points to the right data
   1174     //
   1175     WhoAmI (&mMpService, &ProcessorNumber);
   1176     CpuData = &mMPSystemData.CpuData[ProcessorNumber];
   1177 
   1178     AcquireSpinLock (&CpuData->CpuDataLock);
   1179     CpuData->Procedure = NULL;
   1180     ReleaseSpinLock (&CpuData->CpuDataLock);
   1181   }
   1182 
   1183   AcquireSpinLock (&CpuData->CpuDataLock);
   1184   CpuData->State = CpuStateFinished;
   1185   ReleaseSpinLock (&CpuData->CpuDataLock);
   1186 }
   1187 
   1188 /**
   1189   Function to wake up a specified AP and assign procedure to it.
   1190 
   1191   @param  ProcessorNumber  Handle number of the specified processor.
   1192   @param  Procedure        Procedure to assign.
   1193   @param  ProcArguments    Argument for Procedure.
   1194 
   1195 **/
   1196 VOID
   1197 WakeUpAp (
   1198   IN   UINTN                 ProcessorNumber,
   1199   IN   EFI_AP_PROCEDURE      Procedure,
   1200   IN   VOID                  *ProcArguments
   1201   )
   1202 {
   1203   EFI_STATUS                   Status;
   1204   CPU_DATA_BLOCK               *CpuData;
   1205   EFI_PROCESSOR_INFORMATION    ProcessorInfoBuffer;
   1206 
   1207   ASSERT (ProcessorNumber < mNumberOfProcessors);
   1208   ASSERT (ProcessorNumber < MAX_CPU_NUMBER);
   1209 
   1210   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
   1211 
   1212   AcquireSpinLock (&CpuData->CpuDataLock);
   1213   CpuData->Parameter  = ProcArguments;
   1214   CpuData->Procedure  = Procedure;
   1215   ReleaseSpinLock (&CpuData->CpuDataLock);
   1216 
   1217   Status = GetProcessorInfo (
   1218              &mMpService,
   1219              ProcessorNumber,
   1220              &ProcessorInfoBuffer
   1221              );
   1222   ASSERT_EFI_ERROR (Status);
   1223 
   1224   mExchangeInfo->ApFunction = (VOID *) (UINTN) ApProcWrapper;
   1225   mExchangeInfo->ProcessorNumber[ProcessorInfoBuffer.ProcessorId] = (UINT32) ProcessorNumber;
   1226   SendInitSipiSipi (
   1227     (UINT32) ProcessorInfoBuffer.ProcessorId,
   1228     (UINT32) (UINTN) mStartupVector
   1229     );
   1230 }
   1231 
   1232 /**
   1233   Terminate AP's task and set it to idle state.
   1234 
   1235   This function terminates AP's task due to timeout by sending INIT-SIPI,
   1236   and sends it to idle state.
   1237 
   1238   @param  ProcessorNumber  Handle number of the specified processor.
   1239 
   1240 **/
   1241 VOID
   1242 ResetProcessorToIdleState (
   1243   UINTN      ProcessorNumber
   1244   )
   1245 {
   1246   EFI_STATUS                   Status;
   1247   CPU_DATA_BLOCK               *CpuData;
   1248   EFI_PROCESSOR_INFORMATION    ProcessorInfoBuffer;
   1249 
   1250   Status = GetProcessorInfo (
   1251              &mMpService,
   1252              ProcessorNumber,
   1253              &ProcessorInfoBuffer
   1254              );
   1255   ASSERT_EFI_ERROR (Status);
   1256 
   1257   mExchangeInfo->ApFunction = NULL;
   1258   mExchangeInfo->ProcessorNumber[ProcessorInfoBuffer.ProcessorId] = (UINT32) ProcessorNumber;
   1259   SendInitSipiSipi (
   1260     (UINT32) ProcessorInfoBuffer.ProcessorId,
   1261     (UINT32) (UINTN) mStartupVector
   1262     );
   1263 
   1264   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
   1265 
   1266   AcquireSpinLock (&CpuData->CpuDataLock);
   1267   CpuData->State = CpuStateIdle;
   1268   ReleaseSpinLock (&CpuData->CpuDataLock);
   1269 }
   1270 
   1271 /**
   1272   Worker function of EnableDisableAP ()
   1273 
   1274   Worker function of EnableDisableAP (). Changes state of specified processor.
   1275 
   1276   @param  ProcessorNumber Processor number of specified AP.
   1277   @param  NewState        Desired state of the specified AP.
   1278 
   1279   @retval EFI_SUCCESS     AP's state successfully changed.
   1280 
   1281 **/
   1282 EFI_STATUS
   1283 ChangeCpuState (
   1284   IN     UINTN                      ProcessorNumber,
   1285   IN     BOOLEAN                    NewState
   1286   )
   1287 {
   1288   CPU_DATA_BLOCK  *CpuData;
   1289 
   1290   ASSERT (ProcessorNumber < mNumberOfProcessors);
   1291   ASSERT (ProcessorNumber < MAX_CPU_NUMBER);
   1292 
   1293   CpuData = &mMPSystemData.CpuData[ProcessorNumber];
   1294 
   1295   if (!NewState) {
   1296     AcquireSpinLock (&CpuData->CpuDataLock);
   1297     CpuData->State = CpuStateDisabled;
   1298     ReleaseSpinLock (&CpuData->CpuDataLock);
   1299   } else {
   1300     AcquireSpinLock (&CpuData->CpuDataLock);
   1301     CpuData->State = CpuStateIdle;
   1302     ReleaseSpinLock (&CpuData->CpuDataLock);
   1303   }
   1304 
   1305   return EFI_SUCCESS;
   1306 }
   1307 
   1308 /**
   1309   Test memory region of EfiGcdMemoryTypeReserved.
   1310 
   1311   @param  Length           The length of memory region to test.
   1312 
   1313   @retval EFI_SUCCESS      The memory region passes test.
   1314   @retval EFI_NOT_FOUND    The memory region is not reserved memory.
   1315   @retval EFI_DEVICE_ERROR The memory fails on test.
   1316 
   1317 **/
   1318 EFI_STATUS
   1319 TestReservedMemory (
   1320   UINTN  Length
   1321   )
   1322 {
   1323   EFI_STATUS                       Status;
   1324   EFI_GCD_MEMORY_SPACE_DESCRIPTOR  Descriptor;
   1325   EFI_PHYSICAL_ADDRESS             Address;
   1326   UINTN                            LengthCovered;
   1327   UINTN                            RemainingLength;
   1328 
   1329   //
   1330   // Walk through the memory descriptors covering the memory range.
   1331   //
   1332   Address         = mStartupVector;
   1333   RemainingLength = Length;
   1334   while (Address < mStartupVector + Length) {
   1335     Status = gDS->GetMemorySpaceDescriptor(
   1336                     Address,
   1337                     &Descriptor
   1338                     );
   1339     if (EFI_ERROR (Status)) {
   1340       return EFI_NOT_FOUND;
   1341     }
   1342 
   1343     if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) {
   1344       return EFI_NOT_FOUND;
   1345     }
   1346     //
   1347     // Calculated the length of the intersected range.
   1348     //
   1349     LengthCovered = (UINTN) (Descriptor.BaseAddress + Descriptor.Length - Address);
   1350     if (LengthCovered > RemainingLength) {
   1351       LengthCovered = RemainingLength;
   1352     }
   1353 
   1354     Status = mGenMemoryTest->CompatibleRangeTest (
   1355                                mGenMemoryTest,
   1356                                Address,
   1357                                LengthCovered
   1358                                );
   1359     if (EFI_ERROR (Status)) {
   1360       return EFI_DEVICE_ERROR;
   1361     }
   1362 
   1363     Address         += LengthCovered;
   1364     RemainingLength -= LengthCovered;
   1365   }
   1366 
   1367   return EFI_SUCCESS;
   1368 }
   1369 
   1370 /**
   1371   Allocates startup vector for APs.
   1372 
   1373   This function allocates Startup vector for APs.
   1374 
   1375   @param  Size  The size of startup vector.
   1376 
   1377 **/
   1378 VOID
   1379 AllocateStartupVector (
   1380   UINTN   Size
   1381   )
   1382 {
   1383   EFI_STATUS                            Status;
   1384 
   1385   Status = gBS->LocateProtocol (
   1386                   &gEfiGenericMemTestProtocolGuid,
   1387                   NULL,
   1388                   (VOID **) &mGenMemoryTest
   1389                   );
   1390   if (EFI_ERROR (Status)) {
   1391     mGenMemoryTest = NULL;
   1392   }
   1393 
   1394   for (mStartupVector = 0x7F000; mStartupVector >= 0x2000; mStartupVector -= EFI_PAGE_SIZE) {
   1395     if (mGenMemoryTest != NULL) {
   1396       //
   1397       // Test memory if it is EfiGcdMemoryTypeReserved.
   1398       //
   1399       Status = TestReservedMemory (EFI_SIZE_TO_PAGES (Size) * EFI_PAGE_SIZE);
   1400       if (Status == EFI_DEVICE_ERROR) {
   1401         continue;
   1402       }
   1403     }
   1404 
   1405     Status = gBS->AllocatePages (
   1406                     AllocateAddress,
   1407                     EfiBootServicesCode,
   1408                     EFI_SIZE_TO_PAGES (Size),
   1409                     &mStartupVector
   1410                     );
   1411 
   1412     if (!EFI_ERROR (Status)) {
   1413       break;
   1414     }
   1415   }
   1416 
   1417   ASSERT_EFI_ERROR (Status);
   1418 }
   1419 
   1420 /**
   1421   Prepares Startup Vector for APs.
   1422 
   1423   This function prepares Startup Vector for APs.
   1424 
   1425 **/
   1426 VOID
   1427 PrepareAPStartupVector (
   1428   VOID
   1429   )
   1430 {
   1431   MP_ASSEMBLY_ADDRESS_MAP   AddressMap;
   1432   IA32_DESCRIPTOR           GdtrForBSP;
   1433   IA32_DESCRIPTOR           IdtrForBSP;
   1434   EFI_PHYSICAL_ADDRESS      GdtForAP;
   1435   EFI_PHYSICAL_ADDRESS      IdtForAP;
   1436   EFI_STATUS                Status;
   1437 
   1438   //
   1439   // Get the address map of startup code for AP,
   1440   // including code size, and offset of long jump instructions to redirect.
   1441   //
   1442   AsmGetAddressMap (&AddressMap);
   1443 
   1444   //
   1445   // Allocate a 4K-aligned region under 1M for startup vector for AP.
   1446   // The region contains AP startup code and exchange data between BSP and AP.
   1447   //
   1448   AllocateStartupVector (AddressMap.Size + sizeof (MP_CPU_EXCHANGE_INFO));
   1449 
   1450   //
   1451   // Copy AP startup code to startup vector, and then redirect the long jump
   1452   // instructions for mode switching.
   1453   //
   1454   CopyMem ((VOID *) (UINTN) mStartupVector, AddressMap.RendezvousFunnelAddress, AddressMap.Size);
   1455   *(UINT32 *) (UINTN) (mStartupVector + AddressMap.FlatJumpOffset + 3) = (UINT32) (mStartupVector + AddressMap.PModeEntryOffset);
   1456   //
   1457   // For IA32 mode, LongJumpOffset is filled with zero. If non-zero, then we are in X64 mode, so further redirect for long mode switch.
   1458   //
   1459   if (AddressMap.LongJumpOffset != 0) {
   1460     *(UINT32 *) (UINTN) (mStartupVector + AddressMap.LongJumpOffset + 2) = (UINT32) (mStartupVector + AddressMap.LModeEntryOffset);
   1461   }
   1462 
   1463   //
   1464   // Get the start address of exchange data between BSP and AP.
   1465   //
   1466   mExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) (mStartupVector + AddressMap.Size);
   1467 
   1468   ZeroMem ((VOID *) mExchangeInfo, sizeof (MP_CPU_EXCHANGE_INFO));
   1469 
   1470   mExchangeInfo->StackStart  = AllocatePages (EFI_SIZE_TO_PAGES (mNumberOfProcessors * AP_STACK_SIZE));
   1471   mExchangeInfo->StackSize  = AP_STACK_SIZE;
   1472 
   1473   AsmReadGdtr (&GdtrForBSP);
   1474   AsmReadIdtr (&IdtrForBSP);
   1475 
   1476   //
   1477   // Allocate memory under 4G to hold GDT for APs
   1478   //
   1479   GdtForAP = 0xffffffff;
   1480   Status   = gBS->AllocatePages (
   1481                     AllocateMaxAddress,
   1482                     EfiBootServicesData,
   1483                     EFI_SIZE_TO_PAGES ((GdtrForBSP.Limit + 1) + (IdtrForBSP.Limit + 1)),
   1484                     &GdtForAP
   1485                     );
   1486   ASSERT_EFI_ERROR (Status);
   1487 
   1488   IdtForAP = (UINTN) GdtForAP + GdtrForBSP.Limit + 1;
   1489 
   1490   CopyMem ((VOID *) (UINTN) GdtForAP, (VOID *) GdtrForBSP.Base, GdtrForBSP.Limit + 1);
   1491   CopyMem ((VOID *) (UINTN) IdtForAP, (VOID *) IdtrForBSP.Base, IdtrForBSP.Limit + 1);
   1492 
   1493   mExchangeInfo->GdtrProfile.Base  = (UINTN) GdtForAP;
   1494   mExchangeInfo->GdtrProfile.Limit = GdtrForBSP.Limit;
   1495   mExchangeInfo->IdtrProfile.Base  = (UINTN) IdtForAP;
   1496   mExchangeInfo->IdtrProfile.Limit = IdtrForBSP.Limit;
   1497 
   1498   mExchangeInfo->BufferStart = (UINT32) mStartupVector;
   1499   mExchangeInfo->Cr3         = (UINT32) (AsmReadCr3 ());
   1500 }
   1501 
   1502 /**
   1503   Prepares memory region for processor configuration.
   1504 
   1505   This function prepares memory region for processor configuration.
   1506 
   1507 **/
   1508 VOID
   1509 PrepareMemoryForConfiguration (
   1510   VOID
   1511   )
   1512 {
   1513   UINTN                Index;
   1514 
   1515   //
   1516   // Initialize Spin Locks for system
   1517   //
   1518   InitializeSpinLock (&mMPSystemData.APSerializeLock);
   1519   for (Index = 0; Index < MAX_CPU_NUMBER; Index++) {
   1520     InitializeSpinLock (&mMPSystemData.CpuData[Index].CpuDataLock);
   1521   }
   1522 
   1523   PrepareAPStartupVector ();
   1524 }
   1525 
   1526 /**
   1527   Gets the processor number of BSP.
   1528 
   1529   @return  The processor number of BSP.
   1530 
   1531 **/
   1532 UINTN
   1533 GetBspNumber (
   1534   VOID
   1535   )
   1536 {
   1537   UINTN                      ProcessorNumber;
   1538   EFI_MP_PROC_CONTEXT        ProcessorContextBuffer;
   1539   EFI_STATUS                 Status;
   1540   UINTN                      BufferSize;
   1541 
   1542   BufferSize = sizeof (EFI_MP_PROC_CONTEXT);
   1543 
   1544   for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {
   1545     Status = mFrameworkMpService->GetProcessorContext (
   1546                                     mFrameworkMpService,
   1547                                     ProcessorNumber,
   1548                                     &BufferSize,
   1549                                     &ProcessorContextBuffer
   1550                                     );
   1551     ASSERT_EFI_ERROR (Status);
   1552 
   1553     if (ProcessorContextBuffer.Designation == EfiCpuBSP) {
   1554       break;
   1555     }
   1556   }
   1557   ASSERT (ProcessorNumber < mNumberOfProcessors);
   1558 
   1559   return ProcessorNumber;
   1560 }
   1561 
   1562 /**
   1563   Entrypoint of MP Services Protocol thunk driver.
   1564 
   1565   @param[in] ImageHandle    The firmware allocated handle for the EFI image.
   1566   @param[in] SystemTable    A pointer to the EFI System Table.
   1567 
   1568   @retval EFI_SUCCESS       The entry point is executed successfully.
   1569 
   1570 **/
   1571 EFI_STATUS
   1572 EFIAPI
   1573 InitializeMpServicesProtocol (
   1574   IN EFI_HANDLE        ImageHandle,
   1575   IN EFI_SYSTEM_TABLE  *SystemTable
   1576   )
   1577 {
   1578   EFI_STATUS Status;
   1579 
   1580   //
   1581   // Locates Framework version MP Services Protocol
   1582   //
   1583   Status = gBS->LocateProtocol (
   1584                   &gFrameworkEfiMpServiceProtocolGuid,
   1585                   NULL,
   1586                   (VOID **) &mFrameworkMpService
   1587                   );
   1588   ASSERT_EFI_ERROR (Status);
   1589 
   1590   Status = mFrameworkMpService->GetGeneralMPInfo (
   1591                                   mFrameworkMpService,
   1592                                   &mNumberOfProcessors,
   1593                                   NULL,
   1594                                   NULL,
   1595                                   NULL,
   1596                                   NULL
   1597                                   );
   1598   ASSERT_EFI_ERROR (Status);
   1599   ASSERT (mNumberOfProcessors < MAX_CPU_NUMBER);
   1600 
   1601   PrepareMemoryForConfiguration ();
   1602 
   1603   //
   1604   // Create timer event to check AP state for non-blocking execution.
   1605   //
   1606   Status = gBS->CreateEvent (
   1607                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
   1608                   TPL_CALLBACK,
   1609                   CheckAPsStatus,
   1610                   NULL,
   1611                   &mMPSystemData.CheckAPsEvent
   1612                   );
   1613   ASSERT_EFI_ERROR (Status);
   1614 
   1615   //
   1616   // Now install the MP services protocol.
   1617   //
   1618   Status = gBS->InstallProtocolInterface (
   1619                   &mHandle,
   1620                   &gEfiMpServiceProtocolGuid,
   1621                   EFI_NATIVE_INTERFACE,
   1622                   &mMpService
   1623                   );
   1624   ASSERT_EFI_ERROR (Status);
   1625 
   1626   //
   1627   // Launch the timer event to check AP state.
   1628   //
   1629   Status = gBS->SetTimer (
   1630                   mMPSystemData.CheckAPsEvent,
   1631                   TimerPeriodic,
   1632                   100000
   1633                   );
   1634   ASSERT_EFI_ERROR (Status);
   1635 
   1636   return EFI_SUCCESS;
   1637 }
   1638