Home | History | Annotate | Download | only in PiSmmCpuDxeSmm
      1 /** @file
      2 Implementation of SMM CPU Services Protocol.
      3 
      4 Copyright (c) 2011 - 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 "PiSmmCpuDxeSmm.h"
     16 
     17 //
     18 // SMM CPU Service Protocol instance
     19 //
     20 EFI_SMM_CPU_SERVICE_PROTOCOL  mSmmCpuService = {
     21   SmmGetProcessorInfo,
     22   SmmSwitchBsp,
     23   SmmAddProcessor,
     24   SmmRemoveProcessor,
     25   SmmWhoAmI,
     26   SmmRegisterExceptionHandler
     27 };
     28 
     29 /**
     30   Get Package ID/Core ID/Thread ID of a processor.
     31 
     32   APIC ID must be an initial APIC ID.
     33 
     34   The algorithm below assumes the target system has symmetry across physical package boundaries
     35   with respect to the number of logical processors per package, number of cores per package.
     36 
     37   @param  ApicId    APIC ID of the target logical processor.
     38   @param  Location    Returns the processor location information.
     39 **/
     40 VOID
     41 SmmGetProcessorLocation (
     42   IN UINT32 ApicId,
     43   OUT EFI_CPU_PHYSICAL_LOCATION *Location
     44   )
     45 {
     46   UINTN   ThreadBits;
     47   UINTN   CoreBits;
     48   UINT32  RegEax;
     49   UINT32  RegEbx;
     50   UINT32  RegEcx;
     51   UINT32  RegEdx;
     52   UINT32  MaxCpuIdIndex;
     53   UINT32  SubIndex;
     54   UINTN   LevelType;
     55   UINT32  MaxLogicProcessorsPerPackage;
     56   UINT32  MaxCoresPerPackage;
     57   BOOLEAN TopologyLeafSupported;
     58 
     59   ASSERT (Location != NULL);
     60 
     61   ThreadBits            = 0;
     62   CoreBits              = 0;
     63   TopologyLeafSupported = FALSE;
     64 
     65   //
     66   // Check if the processor is capable of supporting more than one logical processor.
     67   //
     68   AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx);
     69   ASSERT ((RegEdx & BIT28) != 0);
     70 
     71   //
     72   // Assume three-level mapping of APIC ID: Package:Core:SMT.
     73   //
     74 
     75   //
     76   // Get the max index of basic CPUID
     77   //
     78   AsmCpuid (CPUID_SIGNATURE, &MaxCpuIdIndex, NULL, NULL, NULL);
     79 
     80   //
     81   // If the extended topology enumeration leaf is available, it
     82   // is the preferred mechanism for enumerating topology.
     83   //
     84   if (MaxCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) {
     85     AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 0, &RegEax, &RegEbx, &RegEcx, NULL);
     86     //
     87     // If CPUID.(EAX=0BH, ECX=0H):EBX returns zero and maximum input value for
     88     // basic CPUID information is greater than 0BH, then CPUID.0BH leaf is not
     89     // supported on that processor.
     90     //
     91     if ((RegEbx & 0xffff) != 0) {
     92       TopologyLeafSupported = TRUE;
     93 
     94       //
     95       // Sub-leaf index 0 (ECX= 0 as input) provides enumeration parameters to extract
     96       // the SMT sub-field of x2APIC ID.
     97       //
     98       LevelType = (RegEcx >> 8) & 0xff;
     99       ASSERT (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT);
    100       if ((RegEbx & 0xffff) > 1 ) {
    101         ThreadBits = RegEax & 0x1f;
    102       } else {
    103         //
    104         // HT is not supported
    105         //
    106         ThreadBits = 0;
    107       }
    108 
    109       //
    110       // Software must not assume any "level type" encoding
    111       // value to be related to any sub-leaf index, except sub-leaf 0.
    112       //
    113       SubIndex = 1;
    114       do {
    115         AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, SubIndex, &RegEax, NULL, &RegEcx, NULL);
    116         LevelType = (RegEcx >> 8) & 0xff;
    117         if (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE) {
    118           CoreBits = (RegEax & 0x1f) - ThreadBits;
    119           break;
    120         }
    121         SubIndex++;
    122       } while (LevelType != CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID);
    123     }
    124   }
    125 
    126   if (!TopologyLeafSupported) {
    127     AsmCpuid (CPUID_VERSION_INFO, NULL, &RegEbx, NULL, NULL);
    128     MaxLogicProcessorsPerPackage = (RegEbx >> 16) & 0xff;
    129     if (MaxCpuIdIndex >= CPUID_CACHE_PARAMS) {
    130       AsmCpuidEx (CPUID_CACHE_PARAMS, 0, &RegEax, NULL, NULL, NULL);
    131       MaxCoresPerPackage = (RegEax >> 26) + 1;
    132     } else {
    133       //
    134       // Must be a single-core processor.
    135       //
    136       MaxCoresPerPackage = 1;
    137     }
    138 
    139     ThreadBits = (UINTN) (HighBitSet32 (MaxLogicProcessorsPerPackage / MaxCoresPerPackage - 1) + 1);
    140     CoreBits = (UINTN) (HighBitSet32 (MaxCoresPerPackage - 1) + 1);
    141   }
    142 
    143   Location->Thread = ApicId & ~((-1) << ThreadBits);
    144   Location->Core = (ApicId >> ThreadBits) & ~((-1) << CoreBits);
    145   Location->Package = (ApicId >> (ThreadBits+ CoreBits));
    146 }
    147 
    148 /**
    149   Gets processor information on the requested processor at the instant this call is made.
    150 
    151   @param[in]  This                 A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
    152   @param[in]  ProcessorNumber      The handle number of processor.
    153   @param[out] ProcessorInfoBuffer  A pointer to the buffer where information for
    154                                    the requested processor is deposited.
    155 
    156   @retval EFI_SUCCESS             Processor information was returned.
    157   @retval EFI_INVALID_PARAMETER   ProcessorInfoBuffer is NULL.
    158   @retval EFI_INVALID_PARAMETER   ProcessorNumber is invalid.
    159   @retval EFI_NOT_FOUND           The processor with the handle specified by
    160                                   ProcessorNumber does not exist in the platform.
    161 
    162 **/
    163 EFI_STATUS
    164 EFIAPI
    165 SmmGetProcessorInfo (
    166   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This,
    167   IN       UINTN                        ProcessorNumber,
    168   OUT      EFI_PROCESSOR_INFORMATION    *ProcessorInfoBuffer
    169   )
    170 {
    171   //
    172   // Check parameter
    173   //
    174   if (ProcessorNumber >= mMaxNumberOfCpus || ProcessorInfoBuffer == NULL) {
    175     return EFI_INVALID_PARAMETER;
    176   }
    177 
    178   if (gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId == INVALID_APIC_ID) {
    179     return EFI_NOT_FOUND;
    180   }
    181 
    182   //
    183   // Fill in processor information
    184   //
    185   CopyMem (ProcessorInfoBuffer, &gSmmCpuPrivate->ProcessorInfo[ProcessorNumber], sizeof (EFI_PROCESSOR_INFORMATION));
    186   return EFI_SUCCESS;
    187 }
    188 
    189 /**
    190   This service switches the requested AP to be the BSP since the next SMI.
    191 
    192   @param[in] This             A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
    193   @param[in] ProcessorNumber  The handle number of AP that is to become the new BSP.
    194 
    195   @retval EFI_SUCCESS             BSP will be switched in next SMI.
    196   @retval EFI_UNSUPPORTED         Switching the BSP or a processor to be hot-removed is not supported.
    197   @retval EFI_NOT_FOUND           The processor with the handle specified by ProcessorNumber does not exist.
    198   @retval EFI_INVALID_PARAMETER   ProcessorNumber is invalid.
    199 **/
    200 EFI_STATUS
    201 EFIAPI
    202 SmmSwitchBsp (
    203   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This,
    204   IN       UINTN                        ProcessorNumber
    205   )
    206 {
    207   //
    208   // Check parameter
    209   //
    210   if (ProcessorNumber >= mMaxNumberOfCpus) {
    211     return EFI_INVALID_PARAMETER;
    212   }
    213 
    214   if (gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId == INVALID_APIC_ID) {
    215     return EFI_NOT_FOUND;
    216   }
    217 
    218   if (gSmmCpuPrivate->Operation[ProcessorNumber] != SmmCpuNone ||
    219       gSmst->CurrentlyExecutingCpu == ProcessorNumber) {
    220     return EFI_UNSUPPORTED;
    221   }
    222 
    223   //
    224   // Setting of the BSP for next SMI is pending until all SMI handlers are finished
    225   //
    226   gSmmCpuPrivate->Operation[ProcessorNumber] = SmmCpuSwitchBsp;
    227   return EFI_SUCCESS;
    228 }
    229 
    230 /**
    231   Notify that a processor was hot-added.
    232 
    233   @param[in] This                A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
    234   @param[in] ProcessorId         Local APIC ID of the hot-added processor.
    235   @param[out] ProcessorNumber    The handle number of the hot-added processor.
    236 
    237   @retval EFI_SUCCESS            The hot-addition of the specified processors was successfully notified.
    238   @retval EFI_UNSUPPORTED        Hot addition of processor is not supported.
    239   @retval EFI_NOT_FOUND          The processor with the handle specified by ProcessorNumber does not exist.
    240   @retval EFI_INVALID_PARAMETER  ProcessorNumber is invalid.
    241   @retval EFI_ALREADY_STARTED    The processor is already online in the system.
    242 **/
    243 EFI_STATUS
    244 EFIAPI
    245 SmmAddProcessor (
    246   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL  *This,
    247   IN       UINT64                        ProcessorId,
    248   OUT      UINTN                         *ProcessorNumber
    249   )
    250 {
    251   UINTN  Index;
    252 
    253   if (!FeaturePcdGet (PcdCpuHotPlugSupport)) {
    254     return EFI_UNSUPPORTED;
    255   }
    256 
    257   //
    258   // Check parameter
    259   //
    260   if (ProcessorNumber == NULL || ProcessorId == INVALID_APIC_ID) {
    261     return EFI_INVALID_PARAMETER;
    262   }
    263 
    264   //
    265   // Check if the processor already exists
    266   //
    267 
    268   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
    269     if (gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == ProcessorId) {
    270       return EFI_ALREADY_STARTED;
    271     }
    272   }
    273 
    274   //
    275   // Check CPU hot plug data. The CPU RAS handler should have created the mapping
    276   // of the APIC ID to SMBASE.
    277   //
    278   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
    279     if (mCpuHotPlugData.ApicId[Index] == ProcessorId &&
    280         gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == INVALID_APIC_ID) {
    281       gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId = ProcessorId;
    282       gSmmCpuPrivate->ProcessorInfo[Index].StatusFlag = 0;
    283       SmmGetProcessorLocation ((UINT32)ProcessorId, &gSmmCpuPrivate->ProcessorInfo[Index].Location);
    284 
    285       *ProcessorNumber = Index;
    286       gSmmCpuPrivate->Operation[Index] = SmmCpuAdd;
    287       return EFI_SUCCESS;
    288     }
    289   }
    290 
    291   return EFI_INVALID_PARAMETER;
    292 }
    293 
    294 /**
    295   Notify that a processor was hot-removed.
    296 
    297   @param[in] This                A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
    298   @param[in] ProcessorNumber     The handle number of the hot-added processor.
    299 
    300   @retval EFI_SUCCESS            The hot-removal of the specified processors was successfully notified.
    301   @retval EFI_UNSUPPORTED        Hot removal of processor is not supported.
    302   @retval EFI_UNSUPPORTED        Hot removal of BSP is not supported.
    303   @retval EFI_UNSUPPORTED        Hot removal of a processor with pending hot-plug operation is not supported.
    304   @retval EFI_INVALID_PARAMETER  ProcessorNumber is invalid.
    305 **/
    306 EFI_STATUS
    307 EFIAPI
    308 SmmRemoveProcessor (
    309   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL  *This,
    310   IN       UINTN                         ProcessorNumber
    311   )
    312 {
    313   if (!FeaturePcdGet (PcdCpuHotPlugSupport)) {
    314     return EFI_UNSUPPORTED;
    315   }
    316 
    317   //
    318   // Check parameter
    319   //
    320   if (ProcessorNumber >= mMaxNumberOfCpus ||
    321       gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId == INVALID_APIC_ID) {
    322     return EFI_INVALID_PARAMETER;
    323   }
    324 
    325   //
    326   // Can't remove BSP
    327   //
    328   if (ProcessorNumber == gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) {
    329     return EFI_UNSUPPORTED;
    330   }
    331 
    332   if (gSmmCpuPrivate->Operation[ProcessorNumber] != SmmCpuNone) {
    333     return EFI_UNSUPPORTED;
    334   }
    335 
    336   gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId = INVALID_APIC_ID;
    337   mCpuHotPlugData.ApicId[ProcessorNumber] = INVALID_APIC_ID;
    338 
    339   //
    340   // Removal of the processor from the CPU list is pending until all SMI handlers are finished
    341   //
    342   gSmmCpuPrivate->Operation[ProcessorNumber] = SmmCpuRemove;
    343   return EFI_SUCCESS;
    344 }
    345 
    346 /**
    347   This return the handle number for the calling processor.
    348 
    349   @param[in] This                 A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance.
    350   @param[out] ProcessorNumber      The handle number of currently executing processor.
    351 
    352   @retval EFI_SUCCESS             The current processor handle number was returned
    353                                   in ProcessorNumber.
    354   @retval EFI_INVALID_PARAMETER   ProcessorNumber is NULL.
    355 
    356 **/
    357 EFI_STATUS
    358 EFIAPI
    359 SmmWhoAmI (
    360   IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This,
    361   OUT      UINTN                        *ProcessorNumber
    362   )
    363 {
    364   UINTN  Index;
    365   UINT64 ApicId;
    366 
    367   //
    368   // Check parameter
    369   //
    370   if (ProcessorNumber == NULL) {
    371     return EFI_INVALID_PARAMETER;
    372   }
    373 
    374   ApicId = GetApicId ();
    375 
    376   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
    377     if (gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == ApicId) {
    378       *ProcessorNumber = Index;
    379       return EFI_SUCCESS;
    380     }
    381   }
    382   //
    383   // This should not happen
    384   //
    385   ASSERT (FALSE);
    386   return EFI_NOT_FOUND;
    387 }
    388 
    389 /**
    390   Update the SMM CPU list per the pending operation.
    391 
    392   This function is called after return from SMI handlers.
    393 **/
    394 VOID
    395 SmmCpuUpdate (
    396   VOID
    397   )
    398 {
    399   UINTN   Index;
    400 
    401   //
    402   // Handle pending BSP switch operations
    403   //
    404   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
    405     if (gSmmCpuPrivate->Operation[Index] == SmmCpuSwitchBsp) {
    406       gSmmCpuPrivate->Operation[Index] = SmmCpuNone;
    407       mSmmMpSyncData->SwitchBsp = TRUE;
    408       mSmmMpSyncData->CandidateBsp[Index] = TRUE;
    409     }
    410   }
    411 
    412   //
    413   // Handle pending hot-add operations
    414   //
    415   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
    416     if (gSmmCpuPrivate->Operation[Index] == SmmCpuAdd) {
    417       gSmmCpuPrivate->Operation[Index] = SmmCpuNone;
    418       mNumberOfCpus++;
    419     }
    420   }
    421 
    422   //
    423   // Handle pending hot-remove operations
    424   //
    425   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
    426     if (gSmmCpuPrivate->Operation[Index] == SmmCpuRemove) {
    427       gSmmCpuPrivate->Operation[Index] = SmmCpuNone;
    428       mNumberOfCpus--;
    429     }
    430   }
    431 }
    432 
    433 /**
    434   Register exception handler.
    435 
    436   @param  This                  A pointer to the SMM_CPU_SERVICE_PROTOCOL instance.
    437   @param  ExceptionType         Defines which interrupt or exception to hook. Type EFI_EXCEPTION_TYPE and
    438                                 the valid values for this parameter are defined in EFI_DEBUG_SUPPORT_PROTOCOL
    439                                 of the UEFI 2.0 specification.
    440   @param  InterruptHandler      A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER
    441                                 that is called when a processor interrupt occurs.
    442                                 If this parameter is NULL, then the handler will be uninstalled.
    443 
    444   @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
    445   @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was previously installed.
    446   @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not previously installed.
    447   @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
    448 
    449 **/
    450 EFI_STATUS
    451 EFIAPI
    452 SmmRegisterExceptionHandler (
    453     IN EFI_SMM_CPU_SERVICE_PROTOCOL  *This,
    454     IN EFI_EXCEPTION_TYPE            ExceptionType,
    455     IN EFI_CPU_INTERRUPT_HANDLER     InterruptHandler
    456     )
    457 {
    458   return RegisterCpuInterruptHandler (ExceptionType, InterruptHandler);
    459 }
    460 
    461 /**
    462   Initialize SMM CPU Services.
    463 
    464   It installs EFI SMM CPU Services Protocol.
    465 
    466   @param ImageHandle The firmware allocated handle for the EFI image.
    467 
    468   @retval EFI_SUCCESS    EFI SMM CPU Services Protocol was installed successfully.
    469 **/
    470 EFI_STATUS
    471 InitializeSmmCpuServices (
    472   IN EFI_HANDLE  Handle
    473   )
    474 {
    475   EFI_STATUS Status;
    476 
    477   Status = gSmst->SmmInstallProtocolInterface (
    478                     &Handle,
    479                     &gEfiSmmCpuServiceProtocolGuid,
    480                     EFI_NATIVE_INTERFACE,
    481                     &mSmmCpuService
    482                     );
    483   ASSERT_EFI_ERROR (Status);
    484   return Status;
    485 }
    486 
    487