Home | History | Annotate | Download | only in PiSmmCpuDxeSmm
      1 /** @file
      2 Code for Processor S3 restoration
      3 
      4 Copyright (c) 2006 - 2016, 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 typedef struct {
     18   UINTN             Lock;
     19   VOID              *StackStart;
     20   UINTN             StackSize;
     21   VOID              *ApFunction;
     22   IA32_DESCRIPTOR   GdtrProfile;
     23   IA32_DESCRIPTOR   IdtrProfile;
     24   UINT32            BufferStart;
     25   UINT32            Cr3;
     26 } MP_CPU_EXCHANGE_INFO;
     27 
     28 typedef struct {
     29   UINT8 *RendezvousFunnelAddress;
     30   UINTN PModeEntryOffset;
     31   UINTN FlatJumpOffset;
     32   UINTN Size;
     33   UINTN LModeEntryOffset;
     34   UINTN LongJumpOffset;
     35 } MP_ASSEMBLY_ADDRESS_MAP;
     36 
     37 //
     38 // Spin lock used to serialize MemoryMapped operation
     39 //
     40 SPIN_LOCK                *mMemoryMappedLock = NULL;
     41 
     42 /**
     43   Get starting address and size of the rendezvous entry for APs.
     44   Information for fixing a jump instruction in the code is also returned.
     45 
     46   @param AddressMap  Output buffer for address map information.
     47 **/
     48 VOID *
     49 EFIAPI
     50 AsmGetAddressMap (
     51   MP_ASSEMBLY_ADDRESS_MAP                     *AddressMap
     52   );
     53 
     54 #define LEGACY_REGION_SIZE    (2 * 0x1000)
     55 #define LEGACY_REGION_BASE    (0xA0000 - LEGACY_REGION_SIZE)
     56 
     57 ACPI_CPU_DATA                mAcpiCpuData;
     58 volatile UINT32              mNumberToFinish;
     59 MP_CPU_EXCHANGE_INFO         *mExchangeInfo;
     60 BOOLEAN                      mRestoreSmmConfigurationInS3 = FALSE;
     61 VOID                         *mGdtForAp = NULL;
     62 VOID                         *mIdtForAp = NULL;
     63 VOID                         *mMachineCheckHandlerForAp = NULL;
     64 MP_MSR_LOCK                  *mMsrSpinLocks = NULL;
     65 UINTN                        mMsrSpinLockCount;
     66 UINTN                        mMsrCount = 0;
     67 
     68 //
     69 // S3 boot flag
     70 //
     71 BOOLEAN                      mSmmS3Flag = FALSE;
     72 
     73 //
     74 // Pointer to structure used during S3 Resume
     75 //
     76 SMM_S3_RESUME_STATE          *mSmmS3ResumeState = NULL;
     77 
     78 BOOLEAN                      mAcpiS3Enable = TRUE;
     79 
     80 UINT8                        *mApHltLoopCode = NULL;
     81 UINT8                        mApHltLoopCodeTemplate[] = {
     82                                0x8B, 0x44, 0x24, 0x04,  // mov  eax, dword ptr [esp+4]
     83                                0xF0, 0xFF, 0x08,        // lock dec  dword ptr [eax]
     84                                0xFA,                    // cli
     85                                0xF4,                    // hlt
     86                                0xEB, 0xFC               // jmp $-2
     87                                };
     88 
     89 /**
     90   Get MSR spin lock by MSR index.
     91 
     92   @param  MsrIndex       MSR index value.
     93 
     94   @return Pointer to MSR spin lock.
     95 
     96 **/
     97 SPIN_LOCK *
     98 GetMsrSpinLockByIndex (
     99   IN UINT32      MsrIndex
    100   )
    101 {
    102   UINTN     Index;
    103   for (Index = 0; Index < mMsrCount; Index++) {
    104     if (MsrIndex == mMsrSpinLocks[Index].MsrIndex) {
    105       return mMsrSpinLocks[Index].SpinLock;
    106     }
    107   }
    108   return NULL;
    109 }
    110 
    111 /**
    112   Initialize MSR spin lock by MSR index.
    113 
    114   @param  MsrIndex       MSR index value.
    115 
    116 **/
    117 VOID
    118 InitMsrSpinLockByIndex (
    119   IN UINT32      MsrIndex
    120   )
    121 {
    122   UINTN    MsrSpinLockCount;
    123   UINTN    NewMsrSpinLockCount;
    124   UINTN    Index;
    125   UINTN    AddedSize;
    126 
    127   if (mMsrSpinLocks == NULL) {
    128     MsrSpinLockCount = mSmmCpuSemaphores.SemaphoreMsr.AvailableCounter;
    129     mMsrSpinLocks = (MP_MSR_LOCK *) AllocatePool (sizeof (MP_MSR_LOCK) * MsrSpinLockCount);
    130     ASSERT (mMsrSpinLocks != NULL);
    131     for (Index = 0; Index < MsrSpinLockCount; Index++) {
    132       mMsrSpinLocks[Index].SpinLock =
    133        (SPIN_LOCK *)((UINTN)mSmmCpuSemaphores.SemaphoreMsr.Msr + Index * mSemaphoreSize);
    134       mMsrSpinLocks[Index].MsrIndex = (UINT32)-1;
    135     }
    136     mMsrSpinLockCount = MsrSpinLockCount;
    137     mSmmCpuSemaphores.SemaphoreMsr.AvailableCounter = 0;
    138   }
    139   if (GetMsrSpinLockByIndex (MsrIndex) == NULL) {
    140     //
    141     // Initialize spin lock for MSR programming
    142     //
    143     mMsrSpinLocks[mMsrCount].MsrIndex = MsrIndex;
    144     InitializeSpinLock (mMsrSpinLocks[mMsrCount].SpinLock);
    145     mMsrCount ++;
    146     if (mMsrCount == mMsrSpinLockCount) {
    147       //
    148       // If MSR spin lock buffer is full, enlarge it
    149       //
    150       AddedSize = SIZE_4KB;
    151       mSmmCpuSemaphores.SemaphoreMsr.Msr =
    152                         AllocatePages (EFI_SIZE_TO_PAGES(AddedSize));
    153       ASSERT (mSmmCpuSemaphores.SemaphoreMsr.Msr != NULL);
    154       NewMsrSpinLockCount = mMsrSpinLockCount + AddedSize / mSemaphoreSize;
    155       mMsrSpinLocks = ReallocatePool (
    156                         sizeof (MP_MSR_LOCK) * mMsrSpinLockCount,
    157                         sizeof (MP_MSR_LOCK) * NewMsrSpinLockCount,
    158                         mMsrSpinLocks
    159                         );
    160       ASSERT (mMsrSpinLocks != NULL);
    161       mMsrSpinLockCount = NewMsrSpinLockCount;
    162       for (Index = mMsrCount; Index < mMsrSpinLockCount; Index++) {
    163         mMsrSpinLocks[Index].SpinLock =
    164                  (SPIN_LOCK *)((UINTN)mSmmCpuSemaphores.SemaphoreMsr.Msr +
    165                  (Index - mMsrCount)  * mSemaphoreSize);
    166         mMsrSpinLocks[Index].MsrIndex = (UINT32)-1;
    167       }
    168     }
    169   }
    170 }
    171 
    172 /**
    173   Sync up the MTRR values for all processors.
    174 
    175   @param MtrrTable  Table holding fixed/variable MTRR values to be loaded.
    176 **/
    177 VOID
    178 EFIAPI
    179 LoadMtrrData (
    180   EFI_PHYSICAL_ADDRESS       MtrrTable
    181   )
    182 /*++
    183 
    184 Routine Description:
    185 
    186   Sync up the MTRR values for all processors.
    187 
    188 Arguments:
    189 
    190 Returns:
    191     None
    192 
    193 --*/
    194 {
    195   MTRR_SETTINGS   *MtrrSettings;
    196 
    197   MtrrSettings = (MTRR_SETTINGS *) (UINTN) MtrrTable;
    198   MtrrSetAllMtrrs (MtrrSettings);
    199 }
    200 
    201 /**
    202   Programs registers for the calling processor.
    203 
    204   This function programs registers for the calling processor.
    205 
    206   @param  RegisterTable Pointer to register table of the running processor.
    207 
    208 **/
    209 VOID
    210 SetProcessorRegister (
    211   IN CPU_REGISTER_TABLE        *RegisterTable
    212   )
    213 {
    214   CPU_REGISTER_TABLE_ENTRY  *RegisterTableEntry;
    215   UINTN                     Index;
    216   UINTN                     Value;
    217   SPIN_LOCK                 *MsrSpinLock;
    218 
    219   //
    220   // Traverse Register Table of this logical processor
    221   //
    222   RegisterTableEntry = (CPU_REGISTER_TABLE_ENTRY *) (UINTN) RegisterTable->RegisterTableEntry;
    223   for (Index = 0; Index < RegisterTable->TableLength; Index++, RegisterTableEntry++) {
    224     //
    225     // Check the type of specified register
    226     //
    227     switch (RegisterTableEntry->RegisterType) {
    228     //
    229     // The specified register is Control Register
    230     //
    231     case ControlRegister:
    232       switch (RegisterTableEntry->Index) {
    233       case 0:
    234         Value = AsmReadCr0 ();
    235         Value = (UINTN) BitFieldWrite64 (
    236                           Value,
    237                           RegisterTableEntry->ValidBitStart,
    238                           RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1,
    239                           (UINTN) RegisterTableEntry->Value
    240                           );
    241         AsmWriteCr0 (Value);
    242         break;
    243       case 2:
    244         Value = AsmReadCr2 ();
    245         Value = (UINTN) BitFieldWrite64 (
    246                           Value,
    247                           RegisterTableEntry->ValidBitStart,
    248                           RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1,
    249                           (UINTN) RegisterTableEntry->Value
    250                           );
    251         AsmWriteCr2 (Value);
    252         break;
    253       case 3:
    254         Value = AsmReadCr3 ();
    255         Value = (UINTN) BitFieldWrite64 (
    256                           Value,
    257                           RegisterTableEntry->ValidBitStart,
    258                           RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1,
    259                           (UINTN) RegisterTableEntry->Value
    260                           );
    261         AsmWriteCr3 (Value);
    262         break;
    263       case 4:
    264         Value = AsmReadCr4 ();
    265         Value = (UINTN) BitFieldWrite64 (
    266                           Value,
    267                           RegisterTableEntry->ValidBitStart,
    268                           RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1,
    269                           (UINTN) RegisterTableEntry->Value
    270                           );
    271         AsmWriteCr4 (Value);
    272         break;
    273       default:
    274         break;
    275       }
    276       break;
    277     //
    278     // The specified register is Model Specific Register
    279     //
    280     case Msr:
    281       //
    282       // If this function is called to restore register setting after INIT signal,
    283       // there is no need to restore MSRs in register table.
    284       //
    285       if (RegisterTableEntry->ValidBitLength >= 64) {
    286         //
    287         // If length is not less than 64 bits, then directly write without reading
    288         //
    289         AsmWriteMsr64 (
    290           RegisterTableEntry->Index,
    291           RegisterTableEntry->Value
    292           );
    293       } else {
    294         //
    295         // Get lock to avoid Package/Core scope MSRs programming issue in parallel execution mode
    296         // to make sure MSR read/write operation is atomic.
    297         //
    298         MsrSpinLock = GetMsrSpinLockByIndex (RegisterTableEntry->Index);
    299         AcquireSpinLock (MsrSpinLock);
    300         //
    301         // Set the bit section according to bit start and length
    302         //
    303         AsmMsrBitFieldWrite64 (
    304           RegisterTableEntry->Index,
    305           RegisterTableEntry->ValidBitStart,
    306           RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1,
    307           RegisterTableEntry->Value
    308           );
    309         ReleaseSpinLock (MsrSpinLock);
    310       }
    311       break;
    312     //
    313     // MemoryMapped operations
    314     //
    315     case MemoryMapped:
    316       AcquireSpinLock (mMemoryMappedLock);
    317       MmioBitFieldWrite32 (
    318         RegisterTableEntry->Index,
    319         RegisterTableEntry->ValidBitStart,
    320         RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1,
    321         (UINT32)RegisterTableEntry->Value
    322         );
    323       ReleaseSpinLock (mMemoryMappedLock);
    324       break;
    325     //
    326     // Enable or disable cache
    327     //
    328     case CacheControl:
    329       //
    330       // If value of the entry is 0, then disable cache.  Otherwise, enable cache.
    331       //
    332       if (RegisterTableEntry->Value == 0) {
    333         AsmDisableCache ();
    334       } else {
    335         AsmEnableCache ();
    336       }
    337       break;
    338 
    339     default:
    340       break;
    341     }
    342   }
    343 }
    344 
    345 /**
    346   AP initialization before SMBASE relocation in the S3 boot path.
    347 **/
    348 VOID
    349 EarlyMPRendezvousProcedure (
    350   VOID
    351   )
    352 {
    353   CPU_REGISTER_TABLE         *RegisterTableList;
    354   UINT32                     InitApicId;
    355   UINTN                      Index;
    356 
    357   LoadMtrrData (mAcpiCpuData.MtrrTable);
    358 
    359   //
    360   // Find processor number for this CPU.
    361   //
    362   RegisterTableList = (CPU_REGISTER_TABLE *) (UINTN) mAcpiCpuData.PreSmmInitRegisterTable;
    363   InitApicId = GetInitialApicId ();
    364   for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) {
    365     if (RegisterTableList[Index].InitialApicId == InitApicId) {
    366       SetProcessorRegister (&RegisterTableList[Index]);
    367       break;
    368     }
    369   }
    370 
    371   //
    372   // Count down the number with lock mechanism.
    373   //
    374   InterlockedDecrement (&mNumberToFinish);
    375 }
    376 
    377 /**
    378   AP initialization after SMBASE relocation in the S3 boot path.
    379 **/
    380 VOID
    381 MPRendezvousProcedure (
    382   VOID
    383   )
    384 {
    385   CPU_REGISTER_TABLE         *RegisterTableList;
    386   UINT32                     InitApicId;
    387   UINTN                      Index;
    388   UINTN                      TopOfStack;
    389   UINT8                      Stack[128];
    390 
    391   ProgramVirtualWireMode ();
    392   DisableLvtInterrupts ();
    393 
    394   RegisterTableList = (CPU_REGISTER_TABLE *) (UINTN) mAcpiCpuData.RegisterTable;
    395   InitApicId = GetInitialApicId ();
    396   for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) {
    397     if (RegisterTableList[Index].InitialApicId == InitApicId) {
    398       SetProcessorRegister (&RegisterTableList[Index]);
    399       break;
    400     }
    401   }
    402 
    403   //
    404   // Place AP into the safe code, count down the number with lock mechanism in the safe code.
    405   //
    406   TopOfStack  = (UINTN) Stack + sizeof (Stack);
    407   TopOfStack &= ~(UINTN) (CPU_STACK_ALIGNMENT - 1);
    408   CopyMem ((VOID *) (UINTN) mApHltLoopCode, mApHltLoopCodeTemplate, sizeof (mApHltLoopCodeTemplate));
    409   TransferApToSafeState ((UINTN)mApHltLoopCode, TopOfStack, (UINTN)&mNumberToFinish);
    410 }
    411 
    412 /**
    413   Prepares startup vector for APs.
    414 
    415   This function prepares startup vector for APs.
    416 
    417   @param  WorkingBuffer  The address of the work buffer.
    418 **/
    419 VOID
    420 PrepareApStartupVector (
    421   EFI_PHYSICAL_ADDRESS  WorkingBuffer
    422   )
    423 {
    424   EFI_PHYSICAL_ADDRESS                        StartupVector;
    425   MP_ASSEMBLY_ADDRESS_MAP                     AddressMap;
    426 
    427   //
    428   // Get the address map of startup code for AP,
    429   // including code size, and offset of long jump instructions to redirect.
    430   //
    431   ZeroMem (&AddressMap, sizeof (AddressMap));
    432   AsmGetAddressMap (&AddressMap);
    433 
    434   StartupVector = WorkingBuffer;
    435 
    436   //
    437   // Copy AP startup code to startup vector, and then redirect the long jump
    438   // instructions for mode switching.
    439   //
    440   CopyMem ((VOID *) (UINTN) StartupVector, AddressMap.RendezvousFunnelAddress, AddressMap.Size);
    441   *(UINT32 *) (UINTN) (StartupVector + AddressMap.FlatJumpOffset + 3) = (UINT32) (StartupVector + AddressMap.PModeEntryOffset);
    442   if (AddressMap.LongJumpOffset != 0) {
    443     *(UINT32 *) (UINTN) (StartupVector + AddressMap.LongJumpOffset + 2) = (UINT32) (StartupVector + AddressMap.LModeEntryOffset);
    444   }
    445 
    446   //
    447   // Get the start address of exchange data between BSP and AP.
    448   //
    449   mExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) (StartupVector + AddressMap.Size);
    450   ZeroMem ((VOID *) mExchangeInfo, sizeof (MP_CPU_EXCHANGE_INFO));
    451 
    452   CopyMem ((VOID *) (UINTN) &mExchangeInfo->GdtrProfile, (VOID *) (UINTN) mAcpiCpuData.GdtrProfile, sizeof (IA32_DESCRIPTOR));
    453   CopyMem ((VOID *) (UINTN) &mExchangeInfo->IdtrProfile, (VOID *) (UINTN) mAcpiCpuData.IdtrProfile, sizeof (IA32_DESCRIPTOR));
    454 
    455   //
    456   // Copy AP's GDT, IDT and Machine Check handler from SMRAM to ACPI NVS memory
    457   //
    458   CopyMem ((VOID *) mExchangeInfo->GdtrProfile.Base, mGdtForAp, mExchangeInfo->GdtrProfile.Limit + 1);
    459   CopyMem ((VOID *) mExchangeInfo->IdtrProfile.Base, mIdtForAp, mExchangeInfo->IdtrProfile.Limit + 1);
    460   CopyMem ((VOID *)(UINTN) mAcpiCpuData.ApMachineCheckHandlerBase, mMachineCheckHandlerForAp, mAcpiCpuData.ApMachineCheckHandlerSize);
    461 
    462   mExchangeInfo->StackStart  = (VOID *) (UINTN) mAcpiCpuData.StackAddress;
    463   mExchangeInfo->StackSize   = mAcpiCpuData.StackSize;
    464   mExchangeInfo->BufferStart = (UINT32) StartupVector;
    465   mExchangeInfo->Cr3         = (UINT32) (AsmReadCr3 ());
    466 }
    467 
    468 /**
    469   The function is invoked before SMBASE relocation in S3 path to restores CPU status.
    470 
    471   The function is invoked before SMBASE relocation in S3 path. It does first time microcode load
    472   and restores MTRRs for both BSP and APs.
    473 
    474 **/
    475 VOID
    476 EarlyInitializeCpu (
    477   VOID
    478   )
    479 {
    480   CPU_REGISTER_TABLE         *RegisterTableList;
    481   UINT32                     InitApicId;
    482   UINTN                      Index;
    483 
    484   LoadMtrrData (mAcpiCpuData.MtrrTable);
    485 
    486   //
    487   // Find processor number for this CPU.
    488   //
    489   RegisterTableList = (CPU_REGISTER_TABLE *) (UINTN) mAcpiCpuData.PreSmmInitRegisterTable;
    490   InitApicId = GetInitialApicId ();
    491   for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) {
    492     if (RegisterTableList[Index].InitialApicId == InitApicId) {
    493       SetProcessorRegister (&RegisterTableList[Index]);
    494       break;
    495     }
    496   }
    497 
    498   ProgramVirtualWireMode ();
    499 
    500   PrepareApStartupVector (mAcpiCpuData.StartupVector);
    501 
    502   mNumberToFinish = mAcpiCpuData.NumberOfCpus - 1;
    503   mExchangeInfo->ApFunction  = (VOID *) (UINTN) EarlyMPRendezvousProcedure;
    504 
    505   //
    506   // Send INIT IPI - SIPI to all APs
    507   //
    508   SendInitSipiSipiAllExcludingSelf ((UINT32)mAcpiCpuData.StartupVector);
    509 
    510   while (mNumberToFinish > 0) {
    511     CpuPause ();
    512   }
    513 }
    514 
    515 /**
    516   The function is invoked after SMBASE relocation in S3 path to restores CPU status.
    517 
    518   The function is invoked after SMBASE relocation in S3 path. It restores configuration according to
    519   data saved by normal boot path for both BSP and APs.
    520 
    521 **/
    522 VOID
    523 InitializeCpu (
    524   VOID
    525   )
    526 {
    527   CPU_REGISTER_TABLE         *RegisterTableList;
    528   UINT32                     InitApicId;
    529   UINTN                      Index;
    530 
    531   RegisterTableList = (CPU_REGISTER_TABLE *) (UINTN) mAcpiCpuData.RegisterTable;
    532   InitApicId = GetInitialApicId ();
    533   for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) {
    534     if (RegisterTableList[Index].InitialApicId == InitApicId) {
    535       SetProcessorRegister (&RegisterTableList[Index]);
    536       break;
    537     }
    538   }
    539 
    540   mNumberToFinish = mAcpiCpuData.NumberOfCpus - 1;
    541   //
    542   // StackStart was updated when APs were waken up in EarlyInitializeCpu.
    543   // Re-initialize StackAddress to original beginning address.
    544   //
    545   mExchangeInfo->StackStart  = (VOID *) (UINTN) mAcpiCpuData.StackAddress;
    546   mExchangeInfo->ApFunction  = (VOID *) (UINTN) MPRendezvousProcedure;
    547 
    548   //
    549   // Send INIT IPI - SIPI to all APs
    550   //
    551   SendInitSipiSipiAllExcludingSelf ((UINT32)mAcpiCpuData.StartupVector);
    552 
    553   while (mNumberToFinish > 0) {
    554     CpuPause ();
    555   }
    556 }
    557 
    558 /**
    559   Restore SMM Configuration in S3 boot path.
    560 
    561 **/
    562 VOID
    563 RestoreSmmConfigurationInS3 (
    564   VOID
    565   )
    566 {
    567   if (!mAcpiS3Enable) {
    568     return;
    569   }
    570 
    571   //
    572   // Restore SMM Configuration in S3 boot path.
    573   //
    574   if (mRestoreSmmConfigurationInS3) {
    575     //
    576     // Need make sure gSmst is correct because below function may use them.
    577     //
    578     gSmst->SmmStartupThisAp      = gSmmCpuPrivate->SmmCoreEntryContext.SmmStartupThisAp;
    579     gSmst->CurrentlyExecutingCpu = gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu;
    580     gSmst->NumberOfCpus          = gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus;
    581     gSmst->CpuSaveStateSize      = gSmmCpuPrivate->SmmCoreEntryContext.CpuSaveStateSize;
    582     gSmst->CpuSaveState          = gSmmCpuPrivate->SmmCoreEntryContext.CpuSaveState;
    583 
    584     //
    585     // Configure SMM Code Access Check feature if available.
    586     //
    587     ConfigSmmCodeAccessCheck ();
    588 
    589     SmmCpuFeaturesCompleteSmmReadyToLock ();
    590 
    591     mRestoreSmmConfigurationInS3 = FALSE;
    592   }
    593 }
    594 
    595 /**
    596   Perform SMM initialization for all processors in the S3 boot path.
    597 
    598   For a native platform, MP initialization in the S3 boot path is also performed in this function.
    599 **/
    600 VOID
    601 EFIAPI
    602 SmmRestoreCpu (
    603   VOID
    604   )
    605 {
    606   SMM_S3_RESUME_STATE           *SmmS3ResumeState;
    607   IA32_DESCRIPTOR               Ia32Idtr;
    608   IA32_DESCRIPTOR               X64Idtr;
    609   IA32_IDT_GATE_DESCRIPTOR      IdtEntryTable[EXCEPTION_VECTOR_NUMBER];
    610   EFI_STATUS                    Status;
    611 
    612   DEBUG ((EFI_D_INFO, "SmmRestoreCpu()\n"));
    613 
    614   mSmmS3Flag = TRUE;
    615 
    616   InitializeSpinLock (mMemoryMappedLock);
    617 
    618   //
    619   // See if there is enough context to resume PEI Phase
    620   //
    621   if (mSmmS3ResumeState == NULL) {
    622     DEBUG ((EFI_D_ERROR, "No context to return to PEI Phase\n"));
    623     CpuDeadLoop ();
    624   }
    625 
    626   SmmS3ResumeState = mSmmS3ResumeState;
    627   ASSERT (SmmS3ResumeState != NULL);
    628 
    629   if (SmmS3ResumeState->Signature == SMM_S3_RESUME_SMM_64) {
    630     //
    631     // Save the IA32 IDT Descriptor
    632     //
    633     AsmReadIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);
    634 
    635     //
    636     // Setup X64 IDT table
    637     //
    638     ZeroMem (IdtEntryTable, sizeof (IA32_IDT_GATE_DESCRIPTOR) * 32);
    639     X64Idtr.Base = (UINTN) IdtEntryTable;
    640     X64Idtr.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * 32 - 1);
    641     AsmWriteIdtr ((IA32_DESCRIPTOR *) &X64Idtr);
    642 
    643     //
    644     // Setup the default exception handler
    645     //
    646     Status = InitializeCpuExceptionHandlers (NULL);
    647     ASSERT_EFI_ERROR (Status);
    648 
    649     //
    650     // Initialize Debug Agent to support source level debug
    651     //
    652     InitializeDebugAgent (DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64, (VOID *)&Ia32Idtr, NULL);
    653   }
    654 
    655   //
    656   // Skip initialization if mAcpiCpuData is not valid
    657   //
    658   if (mAcpiCpuData.NumberOfCpus > 0) {
    659     //
    660     // First time microcode load and restore MTRRs
    661     //
    662     EarlyInitializeCpu ();
    663   }
    664 
    665   //
    666   // Restore SMBASE for BSP and all APs
    667   //
    668   SmmRelocateBases ();
    669 
    670   //
    671   // Skip initialization if mAcpiCpuData is not valid
    672   //
    673   if (mAcpiCpuData.NumberOfCpus > 0) {
    674     //
    675     // Restore MSRs for BSP and all APs
    676     //
    677     InitializeCpu ();
    678   }
    679 
    680   //
    681   // Set a flag to restore SMM configuration in S3 path.
    682   //
    683   mRestoreSmmConfigurationInS3 = TRUE;
    684 
    685   DEBUG (( EFI_D_INFO, "SMM S3 Return CS                = %x\n", SmmS3ResumeState->ReturnCs));
    686   DEBUG (( EFI_D_INFO, "SMM S3 Return Entry Point       = %x\n", SmmS3ResumeState->ReturnEntryPoint));
    687   DEBUG (( EFI_D_INFO, "SMM S3 Return Context1          = %x\n", SmmS3ResumeState->ReturnContext1));
    688   DEBUG (( EFI_D_INFO, "SMM S3 Return Context2          = %x\n", SmmS3ResumeState->ReturnContext2));
    689   DEBUG (( EFI_D_INFO, "SMM S3 Return Stack Pointer     = %x\n", SmmS3ResumeState->ReturnStackPointer));
    690 
    691   //
    692   // If SMM is in 32-bit mode, then use SwitchStack() to resume PEI Phase
    693   //
    694   if (SmmS3ResumeState->Signature == SMM_S3_RESUME_SMM_32) {
    695     DEBUG ((EFI_D_INFO, "Call SwitchStack() to return to S3 Resume in PEI Phase\n"));
    696 
    697     SwitchStack (
    698       (SWITCH_STACK_ENTRY_POINT)(UINTN)SmmS3ResumeState->ReturnEntryPoint,
    699       (VOID *)(UINTN)SmmS3ResumeState->ReturnContext1,
    700       (VOID *)(UINTN)SmmS3ResumeState->ReturnContext2,
    701       (VOID *)(UINTN)SmmS3ResumeState->ReturnStackPointer
    702       );
    703   }
    704 
    705   //
    706   // If SMM is in 64-bit mode, then use AsmDisablePaging64() to resume PEI Phase
    707   //
    708   if (SmmS3ResumeState->Signature == SMM_S3_RESUME_SMM_64) {
    709     DEBUG ((EFI_D_INFO, "Call AsmDisablePaging64() to return to S3 Resume in PEI Phase\n"));
    710     //
    711     // Disable interrupt of Debug timer, since new IDT table is for IA32 and will not work in long mode.
    712     //
    713     SaveAndSetDebugTimerInterrupt (FALSE);
    714     //
    715     // Restore IA32 IDT table
    716     //
    717     AsmWriteIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);
    718     AsmDisablePaging64 (
    719       SmmS3ResumeState->ReturnCs,
    720       (UINT32)SmmS3ResumeState->ReturnEntryPoint,
    721       (UINT32)SmmS3ResumeState->ReturnContext1,
    722       (UINT32)SmmS3ResumeState->ReturnContext2,
    723       (UINT32)SmmS3ResumeState->ReturnStackPointer
    724       );
    725   }
    726 
    727   //
    728   // Can not resume PEI Phase
    729   //
    730   DEBUG ((EFI_D_ERROR, "No context to return to PEI Phase\n"));
    731   CpuDeadLoop ();
    732 }
    733 
    734 /**
    735   Initialize SMM S3 resume state structure used during S3 Resume.
    736 
    737   @param[in] Cr3    The base address of the page tables to use in SMM.
    738 
    739 **/
    740 VOID
    741 InitSmmS3ResumeState (
    742   IN UINT32  Cr3
    743   )
    744 {
    745   VOID                       *GuidHob;
    746   EFI_SMRAM_DESCRIPTOR       *SmramDescriptor;
    747   SMM_S3_RESUME_STATE        *SmmS3ResumeState;
    748   EFI_PHYSICAL_ADDRESS       Address;
    749   EFI_STATUS                 Status;
    750 
    751   if (!mAcpiS3Enable) {
    752     return;
    753   }
    754 
    755   GuidHob = GetFirstGuidHob (&gEfiAcpiVariableGuid);
    756   if (GuidHob != NULL) {
    757     SmramDescriptor = (EFI_SMRAM_DESCRIPTOR *) GET_GUID_HOB_DATA (GuidHob);
    758 
    759     DEBUG ((EFI_D_INFO, "SMM S3 SMRAM Structure = %x\n", SmramDescriptor));
    760     DEBUG ((EFI_D_INFO, "SMM S3 Structure = %x\n", SmramDescriptor->CpuStart));
    761 
    762     SmmS3ResumeState = (SMM_S3_RESUME_STATE *)(UINTN)SmramDescriptor->CpuStart;
    763     ZeroMem (SmmS3ResumeState, sizeof (SMM_S3_RESUME_STATE));
    764 
    765     mSmmS3ResumeState = SmmS3ResumeState;
    766     SmmS3ResumeState->Smst = (EFI_PHYSICAL_ADDRESS)(UINTN)gSmst;
    767 
    768     SmmS3ResumeState->SmmS3ResumeEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)SmmRestoreCpu;
    769 
    770     SmmS3ResumeState->SmmS3StackSize = SIZE_32KB;
    771     SmmS3ResumeState->SmmS3StackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)SmmS3ResumeState->SmmS3StackSize));
    772     if (SmmS3ResumeState->SmmS3StackBase == 0) {
    773       SmmS3ResumeState->SmmS3StackSize = 0;
    774     }
    775 
    776     SmmS3ResumeState->SmmS3Cr0 = gSmmCr0;
    777     SmmS3ResumeState->SmmS3Cr3 = Cr3;
    778     SmmS3ResumeState->SmmS3Cr4 = gSmmCr4;
    779 
    780     if (sizeof (UINTN) == sizeof (UINT64)) {
    781       SmmS3ResumeState->Signature = SMM_S3_RESUME_SMM_64;
    782     }
    783     if (sizeof (UINTN) == sizeof (UINT32)) {
    784       SmmS3ResumeState->Signature = SMM_S3_RESUME_SMM_32;
    785     }
    786   }
    787 
    788   //
    789   // Patch SmmS3ResumeState->SmmS3Cr3
    790   //
    791   InitSmmS3Cr3 ();
    792 
    793   //
    794   // Allocate safe memory in ACPI NVS for AP to execute hlt loop in
    795   // protected mode on S3 path
    796   //
    797   Address = BASE_4GB - 1;
    798   Status  = gBS->AllocatePages (
    799                    AllocateMaxAddress,
    800                    EfiACPIMemoryNVS,
    801                    EFI_SIZE_TO_PAGES (sizeof (mApHltLoopCodeTemplate)),
    802                    &Address
    803                    );
    804   ASSERT_EFI_ERROR (Status);
    805   mApHltLoopCode = (UINT8 *) (UINTN) Address;
    806 }
    807 
    808 /**
    809   Copy register table from ACPI NVS memory into SMRAM.
    810 
    811   @param[in] DestinationRegisterTableList  Points to destination register table.
    812   @param[in] SourceRegisterTableList       Points to source register table.
    813   @param[in] NumberOfCpus                  Number of CPUs.
    814 
    815 **/
    816 VOID
    817 CopyRegisterTable (
    818   IN CPU_REGISTER_TABLE         *DestinationRegisterTableList,
    819   IN CPU_REGISTER_TABLE         *SourceRegisterTableList,
    820   IN UINT32                     NumberOfCpus
    821   )
    822 {
    823   UINTN                      Index;
    824   UINTN                      Index1;
    825   CPU_REGISTER_TABLE_ENTRY   *RegisterTableEntry;
    826 
    827   CopyMem (DestinationRegisterTableList, SourceRegisterTableList, NumberOfCpus * sizeof (CPU_REGISTER_TABLE));
    828   for (Index = 0; Index < NumberOfCpus; Index++) {
    829     DestinationRegisterTableList[Index].RegisterTableEntry = AllocatePool (DestinationRegisterTableList[Index].AllocatedSize);
    830     ASSERT (DestinationRegisterTableList[Index].RegisterTableEntry != NULL);
    831     CopyMem (DestinationRegisterTableList[Index].RegisterTableEntry, SourceRegisterTableList[Index].RegisterTableEntry, DestinationRegisterTableList[Index].AllocatedSize);
    832     //
    833     // Go though all MSRs in register table to initialize MSR spin lock
    834     //
    835     RegisterTableEntry = DestinationRegisterTableList[Index].RegisterTableEntry;
    836     for (Index1 = 0; Index1 < DestinationRegisterTableList[Index].TableLength; Index1++, RegisterTableEntry++) {
    837       if ((RegisterTableEntry->RegisterType == Msr) && (RegisterTableEntry->ValidBitLength < 64)) {
    838         //
    839         // Initialize MSR spin lock only for those MSRs need bit field writing
    840         //
    841         InitMsrSpinLockByIndex (RegisterTableEntry->Index);
    842       }
    843     }
    844   }
    845 }
    846 
    847 /**
    848   Get ACPI CPU data.
    849 
    850 **/
    851 VOID
    852 GetAcpiCpuData (
    853   VOID
    854   )
    855 {
    856   ACPI_CPU_DATA              *AcpiCpuData;
    857   IA32_DESCRIPTOR            *Gdtr;
    858   IA32_DESCRIPTOR            *Idtr;
    859 
    860   if (!mAcpiS3Enable) {
    861     return;
    862   }
    863 
    864   //
    865   // Prevent use of mAcpiCpuData by initialize NumberOfCpus to 0
    866   //
    867   mAcpiCpuData.NumberOfCpus = 0;
    868 
    869   //
    870   // If PcdCpuS3DataAddress was never set, then do not copy CPU S3 Data into SMRAM
    871   //
    872   AcpiCpuData = (ACPI_CPU_DATA *)(UINTN)PcdGet64 (PcdCpuS3DataAddress);
    873   if (AcpiCpuData == 0) {
    874     return;
    875   }
    876 
    877   //
    878   // For a native platform, copy the CPU S3 data into SMRAM for use on CPU S3 Resume.
    879   //
    880   CopyMem (&mAcpiCpuData, AcpiCpuData, sizeof (mAcpiCpuData));
    881 
    882   mAcpiCpuData.MtrrTable = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (sizeof (MTRR_SETTINGS));
    883   ASSERT (mAcpiCpuData.MtrrTable != 0);
    884 
    885   CopyMem ((VOID *)(UINTN)mAcpiCpuData.MtrrTable, (VOID *)(UINTN)AcpiCpuData->MtrrTable, sizeof (MTRR_SETTINGS));
    886 
    887   mAcpiCpuData.GdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (sizeof (IA32_DESCRIPTOR));
    888   ASSERT (mAcpiCpuData.GdtrProfile != 0);
    889 
    890   CopyMem ((VOID *)(UINTN)mAcpiCpuData.GdtrProfile, (VOID *)(UINTN)AcpiCpuData->GdtrProfile, sizeof (IA32_DESCRIPTOR));
    891 
    892   mAcpiCpuData.IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (sizeof (IA32_DESCRIPTOR));
    893   ASSERT (mAcpiCpuData.IdtrProfile != 0);
    894 
    895   CopyMem ((VOID *)(UINTN)mAcpiCpuData.IdtrProfile, (VOID *)(UINTN)AcpiCpuData->IdtrProfile, sizeof (IA32_DESCRIPTOR));
    896 
    897   mAcpiCpuData.PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (mAcpiCpuData.NumberOfCpus * sizeof (CPU_REGISTER_TABLE));
    898   ASSERT (mAcpiCpuData.PreSmmInitRegisterTable != 0);
    899 
    900   CopyRegisterTable (
    901     (CPU_REGISTER_TABLE *)(UINTN)mAcpiCpuData.PreSmmInitRegisterTable,
    902     (CPU_REGISTER_TABLE *)(UINTN)AcpiCpuData->PreSmmInitRegisterTable,
    903     mAcpiCpuData.NumberOfCpus
    904     );
    905 
    906   mAcpiCpuData.RegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (mAcpiCpuData.NumberOfCpus * sizeof (CPU_REGISTER_TABLE));
    907   ASSERT (mAcpiCpuData.RegisterTable != 0);
    908 
    909   CopyRegisterTable (
    910     (CPU_REGISTER_TABLE *)(UINTN)mAcpiCpuData.RegisterTable,
    911     (CPU_REGISTER_TABLE *)(UINTN)AcpiCpuData->RegisterTable,
    912     mAcpiCpuData.NumberOfCpus
    913     );
    914 
    915   //
    916   // Copy AP's GDT, IDT and Machine Check handler into SMRAM.
    917   //
    918   Gdtr = (IA32_DESCRIPTOR *)(UINTN)mAcpiCpuData.GdtrProfile;
    919   Idtr = (IA32_DESCRIPTOR *)(UINTN)mAcpiCpuData.IdtrProfile;
    920 
    921   mGdtForAp = AllocatePool ((Gdtr->Limit + 1) + (Idtr->Limit + 1) +  mAcpiCpuData.ApMachineCheckHandlerSize);
    922   ASSERT (mGdtForAp != NULL);
    923   mIdtForAp = (VOID *) ((UINTN)mGdtForAp + (Gdtr->Limit + 1));
    924   mMachineCheckHandlerForAp = (VOID *) ((UINTN)mIdtForAp + (Idtr->Limit + 1));
    925 
    926   CopyMem (mGdtForAp, (VOID *)Gdtr->Base, Gdtr->Limit + 1);
    927   CopyMem (mIdtForAp, (VOID *)Idtr->Base, Idtr->Limit + 1);
    928   CopyMem (mMachineCheckHandlerForAp, (VOID *)(UINTN)mAcpiCpuData.ApMachineCheckHandlerBase, mAcpiCpuData.ApMachineCheckHandlerSize);
    929 }
    930 
    931 /**
    932   Get ACPI S3 enable flag.
    933 
    934 **/
    935 VOID
    936 GetAcpiS3EnableFlag (
    937   VOID
    938   )
    939 {
    940   mAcpiS3Enable = PcdGetBool (PcdAcpiS3Enable);
    941 }
    942