Home | History | Annotate | Download | only in CpuMpPei
      1 /** @file
      2   Implementation of Multiple Processor PPI services.
      3 
      4   Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
      5   This program and the accompanying materials
      6   are licensed and made available under the terms and conditions of the BSD License
      7   which accompanies this distribution.  The full text of the license may be found at
      8   http://opensource.org/licenses/bsd-license.php
      9 
     10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "PeiMpServices.h"
     16 
     17 //
     18 // CPU MP PPI to be installed
     19 //
     20 EFI_PEI_MP_SERVICES_PPI                mMpServicesPpi = {
     21   PeiGetNumberOfProcessors,
     22   PeiGetProcessorInfo,
     23   PeiStartupAllAPs,
     24   PeiStartupThisAP,
     25   PeiSwitchBSP,
     26   PeiEnableDisableAP,
     27   PeiWhoAmI,
     28 };
     29 
     30 EFI_PEI_PPI_DESCRIPTOR           mPeiCpuMpPpiDesc = {
     31   (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
     32   &gEfiPeiMpServicesPpiGuid,
     33   &mMpServicesPpi
     34 };
     35 
     36 /**
     37   Get CPU Package/Core/Thread location information.
     38 
     39   @param InitialApicId     CPU APIC ID
     40   @param Location          Pointer to CPU location information
     41 **/
     42 VOID
     43 ExtractProcessorLocation (
     44   IN  UINT32                     InitialApicId,
     45   OUT EFI_CPU_PHYSICAL_LOCATION  *Location
     46   )
     47 {
     48   BOOLEAN  TopologyLeafSupported;
     49   UINTN    ThreadBits;
     50   UINTN    CoreBits;
     51   UINT32   RegEax;
     52   UINT32   RegEbx;
     53   UINT32   RegEcx;
     54   UINT32   RegEdx;
     55   UINT32   MaxCpuIdIndex;
     56   UINT32   SubIndex;
     57   UINTN    LevelType;
     58   UINT32   MaxLogicProcessorsPerPackage;
     59   UINT32   MaxCoresPerPackage;
     60 
     61   //
     62   // Check if the processor is capable of supporting more than one logical processor.
     63   //
     64   AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx);
     65   if ((RegEdx & BIT28) == 0) {
     66     Location->Thread  = 0;
     67     Location->Core    = 0;
     68     Location->Package = 0;
     69     return;
     70   }
     71 
     72   ThreadBits = 0;
     73   CoreBits = 0;
     74 
     75   //
     76   // Assume three-level mapping of APIC ID: Package:Core:SMT.
     77   //
     78 
     79   TopologyLeafSupported = FALSE;
     80   //
     81   // Get the max index of basic CPUID
     82   //
     83   AsmCpuid (CPUID_SIGNATURE, &MaxCpuIdIndex, NULL, NULL, NULL);
     84 
     85   //
     86   // If the extended topology enumeration leaf is available, it
     87   // is the preferred mechanism for enumerating topology.
     88   //
     89   if (MaxCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) {
     90     AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 0, &RegEax, &RegEbx, &RegEcx, NULL);
     91     //
     92     // If CPUID.(EAX=0BH, ECX=0H):EBX returns zero and maximum input value for
     93     // basic CPUID information is greater than 0BH, then CPUID.0BH leaf is not
     94     // supported on that processor.
     95     //
     96     if (RegEbx != 0) {
     97       TopologyLeafSupported = TRUE;
     98 
     99       //
    100       // Sub-leaf index 0 (ECX= 0 as input) provides enumeration parameters to extract
    101       // the SMT sub-field of x2APIC ID.
    102       //
    103       LevelType = (RegEcx >> 8) & 0xff;
    104       ASSERT (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT);
    105       ThreadBits = RegEax & 0x1f;
    106 
    107       //
    108       // Software must not assume any "level type" encoding
    109       // value to be related to any sub-leaf index, except sub-leaf 0.
    110       //
    111       SubIndex = 1;
    112       do {
    113         AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, SubIndex, &RegEax, NULL, &RegEcx, NULL);
    114         LevelType = (RegEcx >> 8) & 0xff;
    115         if (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE) {
    116           CoreBits = (RegEax & 0x1f) - ThreadBits;
    117           break;
    118         }
    119         SubIndex++;
    120       } while (LevelType != CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID);
    121     }
    122   }
    123 
    124   if (!TopologyLeafSupported) {
    125     AsmCpuid (CPUID_VERSION_INFO, NULL, &RegEbx, NULL, NULL);
    126     MaxLogicProcessorsPerPackage = (RegEbx >> 16) & 0xff;
    127     if (MaxCpuIdIndex >= CPUID_CACHE_PARAMS) {
    128       AsmCpuidEx (CPUID_CACHE_PARAMS, 0, &RegEax, NULL, NULL, NULL);
    129       MaxCoresPerPackage = (RegEax >> 26) + 1;
    130     } else {
    131       //
    132       // Must be a single-core processor.
    133       //
    134       MaxCoresPerPackage = 1;
    135     }
    136 
    137     ThreadBits = (UINTN) (HighBitSet32 (MaxLogicProcessorsPerPackage / MaxCoresPerPackage - 1) + 1);
    138     CoreBits = (UINTN) (HighBitSet32 (MaxCoresPerPackage - 1) + 1);
    139   }
    140 
    141   Location->Thread  = InitialApicId & ~((-1) << ThreadBits);
    142   Location->Core    = (InitialApicId >> ThreadBits) & ~((-1) << CoreBits);
    143   Location->Package = (InitialApicId >> (ThreadBits + CoreBits));
    144 }
    145 
    146 /**
    147   Find the current Processor number by APIC ID.
    148 
    149   @param PeiCpuMpData        Pointer to PEI CPU MP Data
    150   @param ProcessorNumber     Return the pocessor number found
    151 
    152   @retval EFI_SUCCESS        ProcessorNumber is found and returned.
    153   @retval EFI_NOT_FOUND      ProcessorNumber is not found.
    154 **/
    155 EFI_STATUS
    156 GetProcessorNumber (
    157   IN PEI_CPU_MP_DATA         *PeiCpuMpData,
    158   OUT UINTN                  *ProcessorNumber
    159   )
    160 {
    161   UINTN                   TotalProcessorNumber;
    162   UINTN                   Index;
    163 
    164   TotalProcessorNumber = PeiCpuMpData->CpuCount;
    165   for (Index = 0; Index < TotalProcessorNumber; Index ++) {
    166     if (PeiCpuMpData->CpuData[Index].ApicId == GetInitialApicId ()) {
    167       *ProcessorNumber = Index;
    168       return EFI_SUCCESS;
    169     }
    170   }
    171   return EFI_NOT_FOUND;
    172 }
    173 
    174 /**
    175   Worker function for SwitchBSP().
    176 
    177   Worker function for SwitchBSP(), assigned to the AP which is intended to become BSP.
    178 
    179   @param Buffer        Pointer to CPU MP Data
    180 **/
    181 VOID
    182 EFIAPI
    183 FutureBSPProc (
    184   IN  VOID                *Buffer
    185   )
    186 {
    187   PEI_CPU_MP_DATA         *DataInHob;
    188 
    189   DataInHob = (PEI_CPU_MP_DATA *) Buffer;
    190   AsmExchangeRole (&DataInHob->APInfo, &DataInHob->BSPInfo);
    191 }
    192 
    193 /**
    194   This service retrieves the number of logical processor in the platform
    195   and the number of those logical processors that are enabled on this boot.
    196   This service may only be called from the BSP.
    197 
    198   This function is used to retrieve the following information:
    199     - The number of logical processors that are present in the system.
    200     - The number of enabled logical processors in the system at the instant
    201       this call is made.
    202 
    203   Because MP Service Ppi provides services to enable and disable processors
    204   dynamically, the number of enabled logical processors may vary during the
    205   course of a boot session.
    206 
    207   If this service is called from an AP, then EFI_DEVICE_ERROR is returned.
    208   If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then
    209   EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors
    210   is returned in NumberOfProcessors, the number of currently enabled processor
    211   is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
    212 
    213   @param[in]  PeiServices         An indirect pointer to the PEI Services Table
    214                                   published by the PEI Foundation.
    215   @param[in]  This                Pointer to this instance of the PPI.
    216   @param[out] NumberOfProcessors  Pointer to the total number of logical processors in
    217                                   the system, including the BSP and disabled APs.
    218   @param[out] NumberOfEnabledProcessors
    219                                   Number of processors in the system that are enabled.
    220 
    221   @retval EFI_SUCCESS             The number of logical processors and enabled
    222                                   logical processors was retrieved.
    223   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
    224   @retval EFI_INVALID_PARAMETER   NumberOfProcessors is NULL.
    225                                   NumberOfEnabledProcessors is NULL.
    226 **/
    227 EFI_STATUS
    228 EFIAPI
    229 PeiGetNumberOfProcessors (
    230   IN  CONST EFI_PEI_SERVICES    **PeiServices,
    231   IN  EFI_PEI_MP_SERVICES_PPI   *This,
    232   OUT UINTN                     *NumberOfProcessors,
    233   OUT UINTN                     *NumberOfEnabledProcessors
    234   )
    235 {
    236   PEI_CPU_MP_DATA         *PeiCpuMpData;
    237   UINTN                   CallerNumber;
    238   UINTN                   ProcessorNumber;
    239   UINTN                   EnabledProcessorNumber;
    240   UINTN                   Index;
    241 
    242   PeiCpuMpData = GetMpHobData ();
    243   if (PeiCpuMpData == NULL) {
    244     return EFI_NOT_FOUND;
    245   }
    246 
    247   if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
    248     return EFI_INVALID_PARAMETER;
    249   }
    250 
    251   //
    252   // Check whether caller processor is BSP
    253   //
    254   PeiWhoAmI (PeiServices, This, &CallerNumber);
    255   if (CallerNumber != PeiCpuMpData->BspNumber) {
    256     return EFI_DEVICE_ERROR;
    257   }
    258 
    259   ProcessorNumber        = PeiCpuMpData->CpuCount;
    260   EnabledProcessorNumber = 0;
    261   for (Index = 0; Index < ProcessorNumber; Index++) {
    262     if (PeiCpuMpData->CpuData[Index].State != CpuStateDisabled) {
    263       EnabledProcessorNumber ++;
    264     }
    265   }
    266 
    267   *NumberOfProcessors = ProcessorNumber;
    268   *NumberOfEnabledProcessors = EnabledProcessorNumber;
    269 
    270   return EFI_SUCCESS;
    271 }
    272 
    273 /**
    274   Gets detailed MP-related information on the requested processor at the
    275   instant this call is made. This service may only be called from the BSP.
    276 
    277   This service retrieves detailed MP-related information about any processor
    278   on the platform. Note the following:
    279     - The processor information may change during the course of a boot session.
    280     - The information presented here is entirely MP related.
    281 
    282   Information regarding the number of caches and their sizes, frequency of operation,
    283   slot numbers is all considered platform-related information and is not provided
    284   by this service.
    285 
    286   @param[in]  PeiServices         An indirect pointer to the PEI Services Table
    287                                   published by the PEI Foundation.
    288   @param[in]  This                Pointer to this instance of the PPI.
    289   @param[in]  ProcessorNumber     Pointer to the total number of logical processors in
    290                                   the system, including the BSP and disabled APs.
    291   @param[out] ProcessorInfoBuffer Number of processors in the system that are enabled.
    292 
    293   @retval EFI_SUCCESS             Processor information was returned.
    294   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
    295   @retval EFI_INVALID_PARAMETER   ProcessorInfoBuffer is NULL.
    296   @retval EFI_NOT_FOUND           The processor with the handle specified by
    297                                   ProcessorNumber does not exist in the platform.
    298 **/
    299 EFI_STATUS
    300 EFIAPI
    301 PeiGetProcessorInfo (
    302   IN  CONST EFI_PEI_SERVICES     **PeiServices,
    303   IN  EFI_PEI_MP_SERVICES_PPI    *This,
    304   IN  UINTN                      ProcessorNumber,
    305   OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
    306   )
    307 {
    308   PEI_CPU_MP_DATA         *PeiCpuMpData;
    309   UINTN                   CallerNumber;
    310 
    311   PeiCpuMpData = GetMpHobData ();
    312   if (PeiCpuMpData == NULL) {
    313     return EFI_NOT_FOUND;
    314   }
    315 
    316   //
    317   // Check whether caller processor is BSP
    318   //
    319   PeiWhoAmI (PeiServices, This, &CallerNumber);
    320   if (CallerNumber != PeiCpuMpData->BspNumber) {
    321     return EFI_DEVICE_ERROR;
    322   }
    323 
    324   if (ProcessorInfoBuffer == NULL) {
    325     return EFI_INVALID_PARAMETER;
    326   }
    327 
    328   if (ProcessorNumber >= PeiCpuMpData->CpuCount) {
    329     return EFI_NOT_FOUND;
    330   }
    331 
    332   ProcessorInfoBuffer->ProcessorId = (UINT64) PeiCpuMpData->CpuData[ProcessorNumber].ApicId;
    333   ProcessorInfoBuffer->StatusFlag  = 0;
    334   if (PeiCpuMpData->CpuData[ProcessorNumber].ApicId == GetInitialApicId()) {
    335     ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT;
    336   }
    337   if (PeiCpuMpData->CpuData[ProcessorNumber].CpuHealthy) {
    338     ProcessorInfoBuffer->StatusFlag |= PROCESSOR_HEALTH_STATUS_BIT;
    339   }
    340   if (PeiCpuMpData->CpuData[ProcessorNumber].State == CpuStateDisabled) {
    341     ProcessorInfoBuffer->StatusFlag &= ~PROCESSOR_ENABLED_BIT;
    342   } else {
    343     ProcessorInfoBuffer->StatusFlag |= PROCESSOR_ENABLED_BIT;
    344   }
    345 
    346   //
    347   // Get processor location information
    348   //
    349   ExtractProcessorLocation (PeiCpuMpData->CpuData[ProcessorNumber].ApicId, &ProcessorInfoBuffer->Location);
    350 
    351   return EFI_SUCCESS;
    352 }
    353 
    354 /**
    355   This service executes a caller provided function on all enabled APs. APs can
    356   run either simultaneously or one at a time in sequence. This service supports
    357   both blocking requests only. This service may only
    358   be called from the BSP.
    359 
    360   This function is used to dispatch all the enabled APs to the function specified
    361   by Procedure.  If any enabled AP is busy, then EFI_NOT_READY is returned
    362   immediately and Procedure is not started on any AP.
    363 
    364   If SingleThread is TRUE, all the enabled APs execute the function specified by
    365   Procedure one by one, in ascending order of processor handle number. Otherwise,
    366   all the enabled APs execute the function specified by Procedure simultaneously.
    367 
    368   If the timeout specified by TimeoutInMicroSeconds expires before all APs return
    369   from Procedure, then Procedure on the failed APs is terminated. All enabled APs
    370   are always available for further calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs()
    371   and EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If FailedCpuList is not NULL, its
    372   content points to the list of processor handle numbers in which Procedure was
    373   terminated.
    374 
    375   Note: It is the responsibility of the consumer of the EFI_PEI_MP_SERVICES_PPI.StartupAllAPs()
    376   to make sure that the nature of the code that is executed on the BSP and the
    377   dispatched APs is well controlled. The MP Services Ppi does not guarantee
    378   that the Procedure function is MP-safe. Hence, the tasks that can be run in
    379   parallel are limited to certain independent tasks and well-controlled exclusive
    380   code. PEI services and Ppis may not be called by APs unless otherwise
    381   specified.
    382 
    383   In blocking execution mode, BSP waits until all APs finish or
    384   TimeoutInMicroSeconds expires.
    385 
    386   @param[in] PeiServices          An indirect pointer to the PEI Services Table
    387                                   published by the PEI Foundation.
    388   @param[in] This                 A pointer to the EFI_PEI_MP_SERVICES_PPI instance.
    389   @param[in] Procedure            A pointer to the function to be run on enabled APs of
    390                                   the system.
    391   @param[in] SingleThread         If TRUE, then all the enabled APs execute the function
    392                                   specified by Procedure one by one, in ascending order
    393                                   of processor handle number. If FALSE, then all the
    394                                   enabled APs execute the function specified by Procedure
    395                                   simultaneously.
    396   @param[in] TimeoutInMicroSeconds
    397                                   Indicates the time limit in microseconds for APs to
    398                                   return from Procedure, for blocking mode only. Zero
    399                                   means infinity. If the timeout expires before all APs
    400                                   return from Procedure, then Procedure on the failed APs
    401                                   is terminated. All enabled APs are available for next
    402                                   function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs()
    403                                   or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the
    404                                   timeout expires in blocking mode, BSP returns
    405                                   EFI_TIMEOUT.
    406   @param[in] ProcedureArgument    The parameter passed into Procedure for all APs.
    407 
    408   @retval EFI_SUCCESS             In blocking mode, all APs have finished before the
    409                                   timeout expired.
    410   @retval EFI_DEVICE_ERROR        Caller processor is AP.
    411   @retval EFI_NOT_STARTED         No enabled APs exist in the system.
    412   @retval EFI_NOT_READY           Any enabled APs are busy.
    413   @retval EFI_TIMEOUT             In blocking mode, the timeout expired before all
    414                                   enabled APs have finished.
    415   @retval EFI_INVALID_PARAMETER   Procedure is NULL.
    416 **/
    417 EFI_STATUS
    418 EFIAPI
    419 PeiStartupAllAPs (
    420   IN  CONST EFI_PEI_SERVICES    **PeiServices,
    421   IN  EFI_PEI_MP_SERVICES_PPI   *This,
    422   IN  EFI_AP_PROCEDURE          Procedure,
    423   IN  BOOLEAN                   SingleThread,
    424   IN  UINTN                     TimeoutInMicroSeconds,
    425   IN  VOID                      *ProcedureArgument      OPTIONAL
    426   )
    427 {
    428   PEI_CPU_MP_DATA         *PeiCpuMpData;
    429   UINTN                   ProcessorNumber;
    430   UINTN                   Index;
    431   UINTN                   CallerNumber;
    432   BOOLEAN                 HasEnabledAp;
    433   BOOLEAN                 HasEnabledIdleAp;
    434   volatile UINT32         *FinishedCount;
    435   EFI_STATUS              Status;
    436   UINTN                   WaitCountIndex;
    437   UINTN                   WaitCountNumber;
    438 
    439   PeiCpuMpData = GetMpHobData ();
    440   if (PeiCpuMpData == NULL) {
    441     return EFI_NOT_FOUND;
    442   }
    443 
    444   if (Procedure == NULL) {
    445     return EFI_INVALID_PARAMETER;
    446   }
    447 
    448   //
    449   // Check whether caller processor is BSP
    450   //
    451   PeiWhoAmI (PeiServices, This, &CallerNumber);
    452   if (CallerNumber != PeiCpuMpData->BspNumber) {
    453     return EFI_DEVICE_ERROR;
    454   }
    455 
    456   ProcessorNumber = PeiCpuMpData->CpuCount;
    457 
    458   HasEnabledAp     = FALSE;
    459   HasEnabledIdleAp = FALSE;
    460   for (Index = 0; Index < ProcessorNumber; Index ++) {
    461     if (Index == CallerNumber) {
    462       //
    463       // Skip BSP
    464       //
    465       continue;
    466     }
    467     if (PeiCpuMpData->CpuData[Index].State != CpuStateDisabled) {
    468       HasEnabledAp = TRUE;
    469       if (PeiCpuMpData->CpuData[Index].State != CpuStateBusy) {
    470         HasEnabledIdleAp = TRUE;
    471       }
    472     }
    473   }
    474   if (!HasEnabledAp) {
    475     //
    476     // If no enabled AP exists, return EFI_NOT_STARTED.
    477     //
    478     return EFI_NOT_STARTED;
    479   }
    480   if (!HasEnabledIdleAp) {
    481     //
    482     // If any enabled APs are busy, return EFI_NOT_READY.
    483     //
    484     return EFI_NOT_READY;
    485   }
    486 
    487   if (PeiCpuMpData->EndOfPeiFlag) {
    488     //
    489     // Backup original data and copy AP reset vector in it
    490     //
    491     BackupAndPrepareWakeupBuffer(PeiCpuMpData);
    492   }
    493 
    494   WaitCountNumber = TimeoutInMicroSeconds / CPU_CHECK_AP_INTERVAL + 1;
    495   WaitCountIndex = 0;
    496   FinishedCount = &PeiCpuMpData->FinishedCount;
    497   if (!SingleThread) {
    498     WakeUpAP (PeiCpuMpData, TRUE, 0, Procedure, ProcedureArgument);
    499     //
    500     // Wait to finish
    501     //
    502     if (TimeoutInMicroSeconds == 0) {
    503       while (*FinishedCount < ProcessorNumber - 1) {
    504         CpuPause ();
    505       }
    506       Status = EFI_SUCCESS;
    507     } else {
    508       Status = EFI_TIMEOUT;
    509       for (WaitCountIndex = 0; WaitCountIndex < WaitCountNumber; WaitCountIndex++) {
    510         MicroSecondDelay (CPU_CHECK_AP_INTERVAL);
    511         if (*FinishedCount >= ProcessorNumber - 1) {
    512           Status = EFI_SUCCESS;
    513           break;
    514         }
    515       }
    516     }
    517   } else {
    518     Status = EFI_SUCCESS;
    519     for (Index = 0; Index < ProcessorNumber; Index++) {
    520       if (Index == CallerNumber) {
    521         continue;
    522       }
    523       WakeUpAP (PeiCpuMpData, FALSE, Index, Procedure, ProcedureArgument);
    524       //
    525       // Wait to finish
    526       //
    527       if (TimeoutInMicroSeconds == 0) {
    528         while (*FinishedCount < 1) {
    529           CpuPause ();
    530         }
    531       } else {
    532         for (WaitCountIndex = 0; WaitCountIndex < WaitCountNumber; WaitCountIndex++) {
    533           MicroSecondDelay (CPU_CHECK_AP_INTERVAL);
    534           if (*FinishedCount >= 1) {
    535             break;
    536           }
    537         }
    538         if (WaitCountIndex == WaitCountNumber) {
    539           Status = EFI_TIMEOUT;
    540         }
    541       }
    542     }
    543   }
    544 
    545   if (PeiCpuMpData->EndOfPeiFlag) {
    546     //
    547     // Restore original data
    548     //
    549     RestoreWakeupBuffer(PeiCpuMpData);
    550   }
    551 
    552   return Status;
    553 }
    554 
    555 /**
    556   This service lets the caller get one enabled AP to execute a caller-provided
    557   function. The caller can request the BSP to wait for the completion
    558   of the AP. This service may only be called from the BSP.
    559 
    560   This function is used to dispatch one enabled AP to the function specified by
    561   Procedure passing in the argument specified by ProcedureArgument.
    562   The execution is in blocking mode. The BSP waits until the AP finishes or
    563   TimeoutInMicroSecondss expires.
    564 
    565   If the timeout specified by TimeoutInMicroseconds expires before the AP returns
    566   from Procedure, then execution of Procedure by the AP is terminated. The AP is
    567   available for subsequent calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() and
    568   EFI_PEI_MP_SERVICES_PPI.StartupThisAP().
    569 
    570   @param[in] PeiServices          An indirect pointer to the PEI Services Table
    571                                   published by the PEI Foundation.
    572   @param[in] This                 A pointer to the EFI_PEI_MP_SERVICES_PPI instance.
    573   @param[in] Procedure            A pointer to the function to be run on enabled APs of
    574                                   the system.
    575   @param[in] ProcessorNumber      The handle number of the AP. The range is from 0 to the
    576                                   total number of logical processors minus 1. The total
    577                                   number of logical processors can be retrieved by
    578                                   EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors().
    579   @param[in] TimeoutInMicroseconds
    580                                   Indicates the time limit in microseconds for APs to
    581                                   return from Procedure, for blocking mode only. Zero
    582                                   means infinity. If the timeout expires before all APs
    583                                   return from Procedure, then Procedure on the failed APs
    584                                   is terminated. All enabled APs are available for next
    585                                   function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs()
    586                                   or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the
    587                                   timeout expires in blocking mode, BSP returns
    588                                   EFI_TIMEOUT.
    589   @param[in] ProcedureArgument    The parameter passed into Procedure for all APs.
    590 
    591   @retval EFI_SUCCESS             In blocking mode, specified AP finished before the
    592                                   timeout expires.
    593   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
    594   @retval EFI_TIMEOUT             In blocking mode, the timeout expired before the
    595                                   specified AP has finished.
    596   @retval EFI_NOT_FOUND           The processor with the handle specified by
    597                                   ProcessorNumber does not exist.
    598   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP or disabled AP.
    599   @retval EFI_INVALID_PARAMETER   Procedure is NULL.
    600 **/
    601 EFI_STATUS
    602 EFIAPI
    603 PeiStartupThisAP (
    604   IN  CONST EFI_PEI_SERVICES    **PeiServices,
    605   IN  EFI_PEI_MP_SERVICES_PPI   *This,
    606   IN  EFI_AP_PROCEDURE          Procedure,
    607   IN  UINTN                     ProcessorNumber,
    608   IN  UINTN                     TimeoutInMicroseconds,
    609   IN  VOID                      *ProcedureArgument      OPTIONAL
    610   )
    611 {
    612   PEI_CPU_MP_DATA         *PeiCpuMpData;
    613   UINTN                   CallerNumber;
    614   volatile UINT32         *FinishedCount;
    615   EFI_STATUS              Status;
    616   UINTN                   WaitCountIndex;
    617   UINTN                   WaitCountNumber;
    618 
    619   PeiCpuMpData = GetMpHobData ();
    620   if (PeiCpuMpData == NULL) {
    621     return EFI_NOT_FOUND;
    622   }
    623 
    624   //
    625   // Check whether caller processor is BSP
    626   //
    627   PeiWhoAmI (PeiServices, This, &CallerNumber);
    628   if (CallerNumber != PeiCpuMpData->BspNumber) {
    629     return EFI_DEVICE_ERROR;
    630   }
    631 
    632   if (ProcessorNumber >= PeiCpuMpData->CpuCount) {
    633     return EFI_NOT_FOUND;
    634   }
    635 
    636   if (ProcessorNumber == PeiCpuMpData->BspNumber || Procedure == NULL) {
    637     return EFI_INVALID_PARAMETER;
    638   }
    639 
    640   //
    641   // Check whether specified AP is disabled
    642   //
    643   if (PeiCpuMpData->CpuData[ProcessorNumber].State == CpuStateDisabled) {
    644     return EFI_INVALID_PARAMETER;
    645   }
    646 
    647   if (PeiCpuMpData->EndOfPeiFlag) {
    648     //
    649     // Backup original data and copy AP reset vector in it
    650     //
    651     BackupAndPrepareWakeupBuffer(PeiCpuMpData);
    652   }
    653 
    654   WaitCountNumber = TimeoutInMicroseconds / CPU_CHECK_AP_INTERVAL + 1;
    655   WaitCountIndex = 0;
    656   FinishedCount = &PeiCpuMpData->FinishedCount;
    657 
    658   WakeUpAP (PeiCpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument);
    659 
    660   //
    661   // Wait to finish
    662   //
    663   if (TimeoutInMicroseconds == 0) {
    664     while (*FinishedCount < 1) {
    665       CpuPause() ;
    666     }
    667     Status = EFI_SUCCESS;
    668   } else {
    669     Status = EFI_TIMEOUT;
    670     for (WaitCountIndex = 0; WaitCountIndex < WaitCountNumber; WaitCountIndex++) {
    671       MicroSecondDelay (CPU_CHECK_AP_INTERVAL);
    672       if (*FinishedCount >= 1) {
    673         Status = EFI_SUCCESS;
    674         break;
    675       }
    676     }
    677   }
    678 
    679   if (PeiCpuMpData->EndOfPeiFlag) {
    680     //
    681     // Backup original data and copy AP reset vector in it
    682     //
    683     RestoreWakeupBuffer(PeiCpuMpData);
    684   }
    685 
    686   return Status;
    687 }
    688 
    689 /**
    690   This service switches the requested AP to be the BSP from that point onward.
    691   This service changes the BSP for all purposes.   This call can only be performed
    692   by the current BSP.
    693 
    694   This service switches the requested AP to be the BSP from that point onward.
    695   This service changes the BSP for all purposes. The new BSP can take over the
    696   execution of the old BSP and continue seamlessly from where the old one left
    697   off.
    698 
    699   If the BSP cannot be switched prior to the return from this service, then
    700   EFI_UNSUPPORTED must be returned.
    701 
    702   @param[in] PeiServices          An indirect pointer to the PEI Services Table
    703                                   published by the PEI Foundation.
    704   @param[in] This                 A pointer to the EFI_PEI_MP_SERVICES_PPI instance.
    705   @param[in] ProcessorNumber      The handle number of the AP. The range is from 0 to the
    706                                   total number of logical processors minus 1. The total
    707                                   number of logical processors can be retrieved by
    708                                   EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors().
    709   @param[in] EnableOldBSP         If TRUE, then the old BSP will be listed as an enabled
    710                                   AP. Otherwise, it will be disabled.
    711 
    712   @retval EFI_SUCCESS             BSP successfully switched.
    713   @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to this
    714                                   service returning.
    715   @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
    716   @retval EFI_SUCCESS             The calling processor is an AP.
    717   @retval EFI_NOT_FOUND           The processor with the handle specified by
    718                                   ProcessorNumber does not exist.
    719   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the current BSP or a disabled
    720                                   AP.
    721   @retval EFI_NOT_READY           The specified AP is busy.
    722 **/
    723 EFI_STATUS
    724 EFIAPI
    725 PeiSwitchBSP (
    726   IN  CONST EFI_PEI_SERVICES   **PeiServices,
    727   IN  EFI_PEI_MP_SERVICES_PPI  *This,
    728   IN  UINTN                    ProcessorNumber,
    729   IN  BOOLEAN                  EnableOldBSP
    730   )
    731 {
    732   PEI_CPU_MP_DATA         *PeiCpuMpData;
    733   UINTN                   CallerNumber;
    734   MSR_IA32_APIC_BASE      ApicBaseMsr;
    735 
    736   PeiCpuMpData = GetMpHobData ();
    737   if (PeiCpuMpData == NULL) {
    738     return EFI_NOT_FOUND;
    739   }
    740 
    741   //
    742   // Check whether caller processor is BSP
    743   //
    744   PeiWhoAmI (PeiServices, This, &CallerNumber);
    745   if (CallerNumber != PeiCpuMpData->BspNumber) {
    746     return EFI_SUCCESS;
    747   }
    748 
    749   if (ProcessorNumber >= PeiCpuMpData->CpuCount) {
    750     return EFI_NOT_FOUND;
    751   }
    752 
    753   //
    754   // Check whether specified AP is disabled
    755   //
    756   if (PeiCpuMpData->CpuData[ProcessorNumber].State == CpuStateDisabled) {
    757     return EFI_INVALID_PARAMETER;
    758   }
    759 
    760   //
    761   // Check whether ProcessorNumber specifies the current BSP
    762   //
    763   if (ProcessorNumber == PeiCpuMpData->BspNumber) {
    764     return EFI_INVALID_PARAMETER;
    765   }
    766 
    767   //
    768   // Check whether specified AP is busy
    769   //
    770   if (PeiCpuMpData->CpuData[ProcessorNumber].State == CpuStateBusy) {
    771     return EFI_NOT_READY;
    772   }
    773 
    774   //
    775   // Clear the BSP bit of MSR_IA32_APIC_BASE
    776   //
    777   ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS);
    778   ApicBaseMsr.Bits.Bsp = 0;
    779   AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64);
    780 
    781   PeiCpuMpData->BSPInfo.State = CPU_SWITCH_STATE_IDLE;
    782   PeiCpuMpData->APInfo.State  = CPU_SWITCH_STATE_IDLE;
    783 
    784   if (PeiCpuMpData->EndOfPeiFlag) {
    785     //
    786     // Backup original data and copy AP reset vector in it
    787     //
    788     BackupAndPrepareWakeupBuffer(PeiCpuMpData);
    789   }
    790 
    791   //
    792   // Need to wakeUp AP (future BSP).
    793   //
    794   WakeUpAP (PeiCpuMpData, FALSE, ProcessorNumber, FutureBSPProc, PeiCpuMpData);
    795 
    796   AsmExchangeRole (&PeiCpuMpData->BSPInfo, &PeiCpuMpData->APInfo);
    797 
    798   if (PeiCpuMpData->EndOfPeiFlag) {
    799     //
    800     // Backup original data and copy AP reset vector in it
    801     //
    802     RestoreWakeupBuffer(PeiCpuMpData);
    803   }
    804 
    805   //
    806   // Set the BSP bit of MSR_IA32_APIC_BASE on new BSP
    807   //
    808   ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS);
    809   ApicBaseMsr.Bits.Bsp = 1;
    810   AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64);
    811   //
    812   // Set old BSP enable state
    813   //
    814   if (!EnableOldBSP) {
    815     PeiCpuMpData->CpuData[PeiCpuMpData->BspNumber].State = CpuStateDisabled;
    816   }
    817   //
    818   // Save new BSP number
    819   //
    820   PeiCpuMpData->BspNumber = (UINT32) ProcessorNumber;
    821 
    822   return EFI_SUCCESS;
    823 }
    824 
    825 /**
    826   This service lets the caller enable or disable an AP from this point onward.
    827   This service may only be called from the BSP.
    828 
    829   This service allows the caller enable or disable an AP from this point onward.
    830   The caller can optionally specify the health status of the AP by Health. If
    831   an AP is being disabled, then the state of the disabled AP is implementation
    832   dependent. If an AP is enabled, then the implementation must guarantee that a
    833   complete initialization sequence is performed on the AP, so the AP is in a state
    834   that is compatible with an MP operating system.
    835 
    836   If the enable or disable AP operation cannot be completed prior to the return
    837   from this service, then EFI_UNSUPPORTED must be returned.
    838 
    839   @param[in] PeiServices          An indirect pointer to the PEI Services Table
    840                                   published by the PEI Foundation.
    841   @param[in] This                 A pointer to the EFI_PEI_MP_SERVICES_PPI instance.
    842   @param[in] ProcessorNumber      The handle number of the AP. The range is from 0 to the
    843                                   total number of logical processors minus 1. The total
    844                                   number of logical processors can be retrieved by
    845                                   EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors().
    846   @param[in] EnableAP             Specifies the new state for the processor for enabled,
    847                                   FALSE for disabled.
    848   @param[in] HealthFlag           If not NULL, a pointer to a value that specifies the
    849                                   new health status of the AP. This flag corresponds to
    850                                   StatusFlag defined in EFI_PEI_MP_SERVICES_PPI.GetProcessorInfo().
    851                                   Only the PROCESSOR_HEALTH_STATUS_BIT is used. All other
    852                                   bits are ignored. If it is NULL, this parameter is
    853                                   ignored.
    854 
    855   @retval EFI_SUCCESS             The specified AP was enabled or disabled successfully.
    856   @retval EFI_UNSUPPORTED         Enabling or disabling an AP cannot be completed prior
    857                                   to this service returning.
    858   @retval EFI_UNSUPPORTED         Enabling or disabling an AP is not supported.
    859   @retval EFI_DEVICE_ERROR        The calling processor is an AP.
    860   @retval EFI_NOT_FOUND           Processor with the handle specified by ProcessorNumber
    861                                   does not exist.
    862   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP.
    863 **/
    864 EFI_STATUS
    865 EFIAPI
    866 PeiEnableDisableAP (
    867   IN  CONST EFI_PEI_SERVICES    **PeiServices,
    868   IN  EFI_PEI_MP_SERVICES_PPI   *This,
    869   IN  UINTN                     ProcessorNumber,
    870   IN  BOOLEAN                   EnableAP,
    871   IN  UINT32                    *HealthFlag OPTIONAL
    872   )
    873 {
    874   PEI_CPU_MP_DATA         *PeiCpuMpData;
    875   UINTN                   CallerNumber;
    876 
    877   PeiCpuMpData = GetMpHobData ();
    878   if (PeiCpuMpData == NULL) {
    879     return EFI_NOT_FOUND;
    880   }
    881 
    882   //
    883   // Check whether caller processor is BSP
    884   //
    885   PeiWhoAmI (PeiServices, This, &CallerNumber);
    886   if (CallerNumber != PeiCpuMpData->BspNumber) {
    887     return EFI_DEVICE_ERROR;
    888   }
    889 
    890   if (ProcessorNumber == PeiCpuMpData->BspNumber) {
    891     return EFI_INVALID_PARAMETER;
    892   }
    893 
    894   if (ProcessorNumber >= PeiCpuMpData->CpuCount) {
    895     return EFI_NOT_FOUND;
    896   }
    897 
    898   if (!EnableAP) {
    899     PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateDisabled;
    900   } else {
    901     PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle;
    902   }
    903 
    904   if (HealthFlag != NULL) {
    905     PeiCpuMpData->CpuData[ProcessorNumber].CpuHealthy =
    906           (BOOLEAN) ((*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT) != 0);
    907   }
    908   return EFI_SUCCESS;
    909 }
    910 
    911 /**
    912   This return the handle number for the calling processor.  This service may be
    913   called from the BSP and APs.
    914 
    915   This service returns the processor handle number for the calling processor.
    916   The returned value is in the range from 0 to the total number of logical
    917   processors minus 1. The total number of logical processors can be retrieved
    918   with EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). This service may be
    919   called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER
    920   is returned. Otherwise, the current processors handle number is returned in
    921   ProcessorNumber, and EFI_SUCCESS is returned.
    922 
    923   @param[in]  PeiServices         An indirect pointer to the PEI Services Table
    924                                   published by the PEI Foundation.
    925   @param[in]  This                A pointer to the EFI_PEI_MP_SERVICES_PPI instance.
    926   @param[out] ProcessorNumber     The handle number of the AP. The range is from 0 to the
    927                                   total number of logical processors minus 1. The total
    928                                   number of logical processors can be retrieved by
    929                                   EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors().
    930 
    931   @retval EFI_SUCCESS             The current processor handle number was returned in
    932                                   ProcessorNumber.
    933   @retval EFI_INVALID_PARAMETER   ProcessorNumber is NULL.
    934 **/
    935 EFI_STATUS
    936 EFIAPI
    937 PeiWhoAmI (
    938   IN  CONST EFI_PEI_SERVICES   **PeiServices,
    939   IN  EFI_PEI_MP_SERVICES_PPI  *This,
    940   OUT UINTN                    *ProcessorNumber
    941   )
    942 {
    943   PEI_CPU_MP_DATA         *PeiCpuMpData;
    944 
    945   PeiCpuMpData = GetMpHobData ();
    946   if (PeiCpuMpData == NULL) {
    947     return EFI_NOT_FOUND;
    948   }
    949 
    950   if (ProcessorNumber == NULL) {
    951     return EFI_INVALID_PARAMETER;
    952   }
    953 
    954   return GetProcessorNumber (PeiCpuMpData, ProcessorNumber);
    955 }
    956 
    957