Home | History | Annotate | Download | only in PiSmmCpuDxeSmm
      1 /** @file
      2 Agent Module to load other modules to deploy SMM Entry Vector for X86 CPU.
      3 
      4 Copyright (c) 2009 - 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 Private Data structure that contains SMM Configuration Protocol
     19 // along its supporting fields.
     20 //
     21 SMM_CPU_PRIVATE_DATA  mSmmCpuPrivateData = {
     22   SMM_CPU_PRIVATE_DATA_SIGNATURE,               // Signature
     23   NULL,                                         // SmmCpuHandle
     24   NULL,                                         // Pointer to ProcessorInfo array
     25   NULL,                                         // Pointer to Operation array
     26   NULL,                                         // Pointer to CpuSaveStateSize array
     27   NULL,                                         // Pointer to CpuSaveState array
     28   { {0} },                                      // SmmReservedSmramRegion
     29   {
     30     SmmStartupThisAp,                           // SmmCoreEntryContext.SmmStartupThisAp
     31     0,                                          // SmmCoreEntryContext.CurrentlyExecutingCpu
     32     0,                                          // SmmCoreEntryContext.NumberOfCpus
     33     NULL,                                       // SmmCoreEntryContext.CpuSaveStateSize
     34     NULL                                        // SmmCoreEntryContext.CpuSaveState
     35   },
     36   NULL,                                         // SmmCoreEntry
     37   {
     38     mSmmCpuPrivateData.SmmReservedSmramRegion,  // SmmConfiguration.SmramReservedRegions
     39     RegisterSmmEntry                            // SmmConfiguration.RegisterSmmEntry
     40   },
     41 };
     42 
     43 CPU_HOT_PLUG_DATA mCpuHotPlugData = {
     44   CPU_HOT_PLUG_DATA_REVISION_1,                 // Revision
     45   0,                                            // Array Length of SmBase and APIC ID
     46   NULL,                                         // Pointer to APIC ID array
     47   NULL,                                         // Pointer to SMBASE array
     48   0,                                            // Reserved
     49   0,                                            // SmrrBase
     50   0                                             // SmrrSize
     51 };
     52 
     53 //
     54 // Global pointer used to access mSmmCpuPrivateData from outside and inside SMM
     55 //
     56 SMM_CPU_PRIVATE_DATA  *gSmmCpuPrivate = &mSmmCpuPrivateData;
     57 
     58 //
     59 // SMM Relocation variables
     60 //
     61 volatile BOOLEAN  *mRebased;
     62 volatile BOOLEAN  mIsBsp;
     63 
     64 ///
     65 /// Handle for the SMM CPU Protocol
     66 ///
     67 EFI_HANDLE  mSmmCpuHandle = NULL;
     68 
     69 ///
     70 /// SMM CPU Protocol instance
     71 ///
     72 EFI_SMM_CPU_PROTOCOL  mSmmCpu  = {
     73   SmmReadSaveState,
     74   SmmWriteSaveState
     75 };
     76 
     77 EFI_CPU_INTERRUPT_HANDLER   mExternalVectorTable[EXCEPTION_VECTOR_NUMBER];
     78 
     79 //
     80 // SMM stack information
     81 //
     82 UINTN mSmmStackArrayBase;
     83 UINTN mSmmStackArrayEnd;
     84 UINTN mSmmStackSize;
     85 
     86 //
     87 // Pointer to structure used during S3 Resume
     88 //
     89 SMM_S3_RESUME_STATE *mSmmS3ResumeState = NULL;
     90 
     91 UINTN mMaxNumberOfCpus = 1;
     92 UINTN mNumberOfCpus = 1;
     93 
     94 //
     95 // SMM ready to lock flag
     96 //
     97 BOOLEAN mSmmReadyToLock = FALSE;
     98 
     99 //
    100 // Global used to cache PCD for SMM Code Access Check enable
    101 //
    102 BOOLEAN                  mSmmCodeAccessCheckEnable = FALSE;
    103 
    104 //
    105 // Spin lock used to serialize setting of SMM Code Access Check feature
    106 //
    107 SPIN_LOCK                mConfigSmmCodeAccessCheckLock;
    108 
    109 /**
    110   Initialize IDT to setup exception handlers for SMM.
    111 
    112 **/
    113 VOID
    114 InitializeSmmIdt (
    115   VOID
    116   )
    117 {
    118   EFI_STATUS               Status;
    119   BOOLEAN                  InterruptState;
    120   IA32_DESCRIPTOR          DxeIdtr;
    121   //
    122   // Disable Interrupt and save DXE IDT table
    123   //
    124   InterruptState = SaveAndDisableInterrupts ();
    125   AsmReadIdtr (&DxeIdtr);
    126   //
    127   // Load SMM temporary IDT table
    128   //
    129   AsmWriteIdtr (&gcSmiIdtr);
    130   //
    131   // Setup SMM default exception handlers, SMM IDT table
    132   // will be updated and saved in gcSmiIdtr
    133   //
    134   Status = InitializeCpuExceptionHandlers (NULL);
    135   ASSERT_EFI_ERROR (Status);
    136   //
    137   // Restore DXE IDT table and CPU interrupt
    138   //
    139   AsmWriteIdtr ((IA32_DESCRIPTOR *) &DxeIdtr);
    140   SetInterruptState (InterruptState);
    141 }
    142 
    143 /**
    144   Search module name by input IP address and output it.
    145 
    146   @param CallerIpAddress   Caller instruction pointer.
    147 
    148 **/
    149 VOID
    150 DumpModuleInfoByIp (
    151   IN  UINTN              CallerIpAddress
    152   )
    153 {
    154   UINTN                                Pe32Data;
    155   EFI_IMAGE_DOS_HEADER                 *DosHdr;
    156   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION  Hdr;
    157   VOID                                 *PdbPointer;
    158   UINT64                               DumpIpAddress;
    159 
    160   //
    161   // Find Image Base
    162   //
    163   Pe32Data = CallerIpAddress & ~(SIZE_4KB - 1);
    164   while (Pe32Data != 0) {
    165     DosHdr = (EFI_IMAGE_DOS_HEADER *) Pe32Data;
    166     if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
    167       //
    168       // DOS image header is present, so read the PE header after the DOS image header.
    169       //
    170       Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)(Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
    171       //
    172       // Make sure PE header address does not overflow and is less than the initial address.
    173       //
    174       if (((UINTN)Hdr.Pe32 > Pe32Data) && ((UINTN)Hdr.Pe32 < CallerIpAddress)) {
    175         if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
    176           //
    177           // It's PE image.
    178           //
    179           break;
    180         }
    181       }
    182     }
    183 
    184     //
    185     // Not found the image base, check the previous aligned address
    186     //
    187     Pe32Data -= SIZE_4KB;
    188   }
    189 
    190   DumpIpAddress = CallerIpAddress;
    191   DEBUG ((EFI_D_ERROR, "It is invoked from the instruction before IP(0x%lx)", DumpIpAddress));
    192 
    193   if (Pe32Data != 0) {
    194     PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *) Pe32Data);
    195     if (PdbPointer != NULL) {
    196       DEBUG ((EFI_D_ERROR, " in module (%a)", PdbPointer));
    197     }
    198   }
    199 }
    200 
    201 /**
    202   Read information from the CPU save state.
    203 
    204   @param  This      EFI_SMM_CPU_PROTOCOL instance
    205   @param  Width     The number of bytes to read from the CPU save state.
    206   @param  Register  Specifies the CPU register to read form the save state.
    207   @param  CpuIndex  Specifies the zero-based index of the CPU save state.
    208   @param  Buffer    Upon return, this holds the CPU register value read from the save state.
    209 
    210   @retval EFI_SUCCESS   The register was read from Save State
    211   @retval EFI_NOT_FOUND The register is not defined for the Save State of Processor
    212   @retval EFI_INVALID_PARAMTER   This or Buffer is NULL.
    213 
    214 **/
    215 EFI_STATUS
    216 EFIAPI
    217 SmmReadSaveState (
    218   IN CONST EFI_SMM_CPU_PROTOCOL         *This,
    219   IN UINTN                              Width,
    220   IN EFI_SMM_SAVE_STATE_REGISTER        Register,
    221   IN UINTN                              CpuIndex,
    222   OUT VOID                              *Buffer
    223   )
    224 {
    225   EFI_STATUS  Status;
    226 
    227   //
    228   // Retrieve pointer to the specified CPU's SMM Save State buffer
    229   //
    230   if ((CpuIndex >= gSmst->NumberOfCpus) || (Buffer == NULL)) {
    231     return EFI_INVALID_PARAMETER;
    232   }
    233 
    234   //
    235   // Check for special EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID
    236   //
    237   if (Register == EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID) {
    238     //
    239     // The pseudo-register only supports the 64-bit size specified by Width.
    240     //
    241     if (Width != sizeof (UINT64)) {
    242       return EFI_INVALID_PARAMETER;
    243     }
    244     //
    245     // If the processor is in SMM at the time the SMI occurred,
    246     // the pseudo register value for EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID is returned in Buffer.
    247     // Otherwise, EFI_NOT_FOUND is returned.
    248     //
    249     if (mSmmMpSyncData->CpuData[CpuIndex].Present) {
    250       *(UINT64 *)Buffer = gSmmCpuPrivate->ProcessorInfo[CpuIndex].ProcessorId;
    251       return EFI_SUCCESS;
    252     } else {
    253       return EFI_NOT_FOUND;
    254     }
    255   }
    256 
    257   if (!mSmmMpSyncData->CpuData[CpuIndex].Present) {
    258     return EFI_INVALID_PARAMETER;
    259   }
    260 
    261   Status = SmmCpuFeaturesReadSaveStateRegister (CpuIndex, Register, Width, Buffer);
    262   if (Status == EFI_UNSUPPORTED) {
    263     Status = ReadSaveStateRegister (CpuIndex, Register, Width, Buffer);
    264   }
    265   return Status;
    266 }
    267 
    268 /**
    269   Write data to the CPU save state.
    270 
    271   @param  This      EFI_SMM_CPU_PROTOCOL instance
    272   @param  Width     The number of bytes to read from the CPU save state.
    273   @param  Register  Specifies the CPU register to write to the save state.
    274   @param  CpuIndex  Specifies the zero-based index of the CPU save state
    275   @param  Buffer    Upon entry, this holds the new CPU register value.
    276 
    277   @retval EFI_SUCCESS   The register was written from Save State
    278   @retval EFI_NOT_FOUND The register is not defined for the Save State of Processor
    279   @retval EFI_INVALID_PARAMTER   ProcessorIndex or Width is not correct
    280 
    281 **/
    282 EFI_STATUS
    283 EFIAPI
    284 SmmWriteSaveState (
    285   IN CONST EFI_SMM_CPU_PROTOCOL         *This,
    286   IN UINTN                              Width,
    287   IN EFI_SMM_SAVE_STATE_REGISTER        Register,
    288   IN UINTN                              CpuIndex,
    289   IN CONST VOID                         *Buffer
    290   )
    291 {
    292   EFI_STATUS  Status;
    293 
    294   //
    295   // Retrieve pointer to the specified CPU's SMM Save State buffer
    296   //
    297   if ((CpuIndex >= gSmst->NumberOfCpus) || (Buffer == NULL)) {
    298     return EFI_INVALID_PARAMETER;
    299   }
    300 
    301   //
    302   // Writes to EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID are ignored
    303   //
    304   if (Register == EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID) {
    305     return EFI_SUCCESS;
    306   }
    307 
    308   if (!mSmmMpSyncData->CpuData[CpuIndex].Present) {
    309     return EFI_INVALID_PARAMETER;
    310   }
    311 
    312   Status = SmmCpuFeaturesWriteSaveStateRegister (CpuIndex, Register, Width, Buffer);
    313   if (Status == EFI_UNSUPPORTED) {
    314     Status = WriteSaveStateRegister (CpuIndex, Register, Width, Buffer);
    315   }
    316   return Status;
    317 }
    318 
    319 
    320 /**
    321   C function for SMI handler. To change all processor's SMMBase Register.
    322 
    323 **/
    324 VOID
    325 EFIAPI
    326 SmmInitHandler (
    327   VOID
    328   )
    329 {
    330   UINT32                            ApicId;
    331   UINTN                             Index;
    332 
    333   //
    334   // Update SMM IDT entries' code segment and load IDT
    335   //
    336   AsmWriteIdtr (&gcSmiIdtr);
    337   ApicId = GetApicId ();
    338 
    339   ASSERT (mNumberOfCpus <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
    340 
    341   for (Index = 0; Index < mNumberOfCpus; Index++) {
    342     if (ApicId == (UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId) {
    343       //
    344       // Initialize SMM specific features on the currently executing CPU
    345       //
    346       SmmCpuFeaturesInitializeProcessor (
    347         Index,
    348         mIsBsp,
    349         gSmmCpuPrivate->ProcessorInfo,
    350         &mCpuHotPlugData
    351         );
    352 
    353       if (mIsBsp) {
    354         //
    355         // BSP rebase is already done above.
    356         // Initialize private data during S3 resume
    357         //
    358         InitializeMpSyncData ();
    359       }
    360 
    361       //
    362       // Hook return after RSM to set SMM re-based flag
    363       //
    364       SemaphoreHook (Index, &mRebased[Index]);
    365 
    366       return;
    367     }
    368   }
    369   ASSERT (FALSE);
    370 }
    371 
    372 /**
    373   Relocate SmmBases for each processor.
    374 
    375   Execute on first boot and all S3 resumes
    376 
    377 **/
    378 VOID
    379 EFIAPI
    380 SmmRelocateBases (
    381   VOID
    382   )
    383 {
    384   UINT8                 BakBuf[BACK_BUF_SIZE];
    385   SMRAM_SAVE_STATE_MAP  BakBuf2;
    386   SMRAM_SAVE_STATE_MAP  *CpuStatePtr;
    387   UINT8                 *U8Ptr;
    388   UINT32                ApicId;
    389   UINTN                 Index;
    390   UINTN                 BspIndex;
    391 
    392   //
    393   // Make sure the reserved size is large enough for procedure SmmInitTemplate.
    394   //
    395   ASSERT (sizeof (BakBuf) >= gcSmmInitSize);
    396 
    397   //
    398   // Patch ASM code template with current CR0, CR3, and CR4 values
    399   //
    400   gSmmCr0 = (UINT32)AsmReadCr0 ();
    401   gSmmCr3 = (UINT32)AsmReadCr3 ();
    402   gSmmCr4 = (UINT32)AsmReadCr4 ();
    403 
    404   //
    405   // Patch GDTR for SMM base relocation
    406   //
    407   gcSmiInitGdtr.Base  = gcSmiGdtr.Base;
    408   gcSmiInitGdtr.Limit = gcSmiGdtr.Limit;
    409 
    410   U8Ptr = (UINT8*)(UINTN)(SMM_DEFAULT_SMBASE + SMM_HANDLER_OFFSET);
    411   CpuStatePtr = (SMRAM_SAVE_STATE_MAP *)(UINTN)(SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET);
    412 
    413   //
    414   // Backup original contents at address 0x38000
    415   //
    416   CopyMem (BakBuf, U8Ptr, sizeof (BakBuf));
    417   CopyMem (&BakBuf2, CpuStatePtr, sizeof (BakBuf2));
    418 
    419   //
    420   // Load image for relocation
    421   //
    422   CopyMem (U8Ptr, gcSmmInitTemplate, gcSmmInitSize);
    423 
    424   //
    425   // Retrieve the local APIC ID of current processor
    426   //
    427   ApicId = GetApicId ();
    428 
    429   //
    430   // Relocate SM bases for all APs
    431   // This is APs' 1st SMI - rebase will be done here, and APs' default SMI handler will be overridden by gcSmmInitTemplate
    432   //
    433   mIsBsp   = FALSE;
    434   BspIndex = (UINTN)-1;
    435   for (Index = 0; Index < mNumberOfCpus; Index++) {
    436     mRebased[Index] = FALSE;
    437     if (ApicId != (UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId) {
    438       SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId);
    439       //
    440       // Wait for this AP to finish its 1st SMI
    441       //
    442       while (!mRebased[Index]);
    443     } else {
    444       //
    445       // BSP will be Relocated later
    446       //
    447       BspIndex = Index;
    448     }
    449   }
    450 
    451   //
    452   // Relocate BSP's SMM base
    453   //
    454   ASSERT (BspIndex != (UINTN)-1);
    455   mIsBsp = TRUE;
    456   SendSmiIpi (ApicId);
    457   //
    458   // Wait for the BSP to finish its 1st SMI
    459   //
    460   while (!mRebased[BspIndex]);
    461 
    462   //
    463   // Restore contents at address 0x38000
    464   //
    465   CopyMem (CpuStatePtr, &BakBuf2, sizeof (BakBuf2));
    466   CopyMem (U8Ptr, BakBuf, sizeof (BakBuf));
    467 }
    468 
    469 /**
    470   Perform SMM initialization for all processors in the S3 boot path.
    471 
    472   For a native platform, MP initialization in the S3 boot path is also performed in this function.
    473 **/
    474 VOID
    475 EFIAPI
    476 SmmRestoreCpu (
    477   VOID
    478   )
    479 {
    480   SMM_S3_RESUME_STATE           *SmmS3ResumeState;
    481   IA32_DESCRIPTOR               Ia32Idtr;
    482   IA32_DESCRIPTOR               X64Idtr;
    483   IA32_IDT_GATE_DESCRIPTOR      IdtEntryTable[EXCEPTION_VECTOR_NUMBER];
    484   EFI_STATUS                    Status;
    485 
    486   DEBUG ((EFI_D_INFO, "SmmRestoreCpu()\n"));
    487 
    488   //
    489   // See if there is enough context to resume PEI Phase
    490   //
    491   if (mSmmS3ResumeState == NULL) {
    492     DEBUG ((EFI_D_ERROR, "No context to return to PEI Phase\n"));
    493     CpuDeadLoop ();
    494   }
    495 
    496   SmmS3ResumeState = mSmmS3ResumeState;
    497   ASSERT (SmmS3ResumeState != NULL);
    498 
    499   if (SmmS3ResumeState->Signature == SMM_S3_RESUME_SMM_64) {
    500     //
    501     // Save the IA32 IDT Descriptor
    502     //
    503     AsmReadIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);
    504 
    505     //
    506     // Setup X64 IDT table
    507     //
    508     ZeroMem (IdtEntryTable, sizeof (IA32_IDT_GATE_DESCRIPTOR) * 32);
    509     X64Idtr.Base = (UINTN) IdtEntryTable;
    510     X64Idtr.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * 32 - 1);
    511     AsmWriteIdtr ((IA32_DESCRIPTOR *) &X64Idtr);
    512 
    513     //
    514     // Setup the default exception handler
    515     //
    516     Status = InitializeCpuExceptionHandlers (NULL);
    517     ASSERT_EFI_ERROR (Status);
    518 
    519     //
    520     // Initialize Debug Agent to support source level debug
    521     //
    522     InitializeDebugAgent (DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64, (VOID *)&Ia32Idtr, NULL);
    523   }
    524 
    525   //
    526   // Skip initialization if mAcpiCpuData is not valid
    527   //
    528   if (mAcpiCpuData.NumberOfCpus > 0) {
    529     //
    530     // First time microcode load and restore MTRRs
    531     //
    532     EarlyInitializeCpu ();
    533   }
    534 
    535   //
    536   // Restore SMBASE for BSP and all APs
    537   //
    538   SmmRelocateBases ();
    539 
    540   //
    541   // Skip initialization if mAcpiCpuData is not valid
    542   //
    543   if (mAcpiCpuData.NumberOfCpus > 0) {
    544     //
    545     // Restore MSRs for BSP and all APs
    546     //
    547     InitializeCpu ();
    548   }
    549 
    550   //
    551   // Set a flag to restore SMM configuration in S3 path.
    552   //
    553   mRestoreSmmConfigurationInS3 = TRUE;
    554 
    555   DEBUG (( EFI_D_INFO, "SMM S3 Return CS                = %x\n", SmmS3ResumeState->ReturnCs));
    556   DEBUG (( EFI_D_INFO, "SMM S3 Return Entry Point       = %x\n", SmmS3ResumeState->ReturnEntryPoint));
    557   DEBUG (( EFI_D_INFO, "SMM S3 Return Context1          = %x\n", SmmS3ResumeState->ReturnContext1));
    558   DEBUG (( EFI_D_INFO, "SMM S3 Return Context2          = %x\n", SmmS3ResumeState->ReturnContext2));
    559   DEBUG (( EFI_D_INFO, "SMM S3 Return Stack Pointer     = %x\n", SmmS3ResumeState->ReturnStackPointer));
    560 
    561   //
    562   // If SMM is in 32-bit mode, then use SwitchStack() to resume PEI Phase
    563   //
    564   if (SmmS3ResumeState->Signature == SMM_S3_RESUME_SMM_32) {
    565     DEBUG ((EFI_D_INFO, "Call SwitchStack() to return to S3 Resume in PEI Phase\n"));
    566 
    567     SwitchStack (
    568       (SWITCH_STACK_ENTRY_POINT)(UINTN)SmmS3ResumeState->ReturnEntryPoint,
    569       (VOID *)(UINTN)SmmS3ResumeState->ReturnContext1,
    570       (VOID *)(UINTN)SmmS3ResumeState->ReturnContext2,
    571       (VOID *)(UINTN)SmmS3ResumeState->ReturnStackPointer
    572       );
    573   }
    574 
    575   //
    576   // If SMM is in 64-bit mode, then use AsmDisablePaging64() to resume PEI Phase
    577   //
    578   if (SmmS3ResumeState->Signature == SMM_S3_RESUME_SMM_64) {
    579     DEBUG ((EFI_D_INFO, "Call AsmDisablePaging64() to return to S3 Resume in PEI Phase\n"));
    580     //
    581     // Disable interrupt of Debug timer, since new IDT table is for IA32 and will not work in long mode.
    582     //
    583     SaveAndSetDebugTimerInterrupt (FALSE);
    584     //
    585     // Restore IA32 IDT table
    586     //
    587     AsmWriteIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);
    588     AsmDisablePaging64 (
    589       SmmS3ResumeState->ReturnCs,
    590       (UINT32)SmmS3ResumeState->ReturnEntryPoint,
    591       (UINT32)SmmS3ResumeState->ReturnContext1,
    592       (UINT32)SmmS3ResumeState->ReturnContext2,
    593       (UINT32)SmmS3ResumeState->ReturnStackPointer
    594       );
    595   }
    596 
    597   //
    598   // Can not resume PEI Phase
    599   //
    600   DEBUG ((EFI_D_ERROR, "No context to return to PEI Phase\n"));
    601   CpuDeadLoop ();
    602 }
    603 
    604 /**
    605   Copy register table from ACPI NVS memory into SMRAM.
    606 
    607   @param[in] DestinationRegisterTableList  Points to destination register table.
    608   @param[in] SourceRegisterTableList       Points to source register table.
    609   @param[in] NumberOfCpus                  Number of CPUs.
    610 
    611 **/
    612 VOID
    613 CopyRegisterTable (
    614   IN CPU_REGISTER_TABLE         *DestinationRegisterTableList,
    615   IN CPU_REGISTER_TABLE         *SourceRegisterTableList,
    616   IN UINT32                     NumberOfCpus
    617   )
    618 {
    619   UINTN                      Index;
    620   UINTN                      Index1;
    621   CPU_REGISTER_TABLE_ENTRY   *RegisterTableEntry;
    622 
    623   CopyMem (DestinationRegisterTableList, SourceRegisterTableList, NumberOfCpus * sizeof (CPU_REGISTER_TABLE));
    624   for (Index = 0; Index < NumberOfCpus; Index++) {
    625     DestinationRegisterTableList[Index].RegisterTableEntry = AllocatePool (DestinationRegisterTableList[Index].AllocatedSize);
    626     ASSERT (DestinationRegisterTableList[Index].RegisterTableEntry != NULL);
    627     CopyMem (DestinationRegisterTableList[Index].RegisterTableEntry, SourceRegisterTableList[Index].RegisterTableEntry, DestinationRegisterTableList[Index].AllocatedSize);
    628     //
    629     // Go though all MSRs in register table to initialize MSR spin lock
    630     //
    631     RegisterTableEntry = DestinationRegisterTableList[Index].RegisterTableEntry;
    632     for (Index1 = 0; Index1 < DestinationRegisterTableList[Index].TableLength; Index1++, RegisterTableEntry++) {
    633       if ((RegisterTableEntry->RegisterType == Msr) && (RegisterTableEntry->ValidBitLength < 64)) {
    634         //
    635         // Initialize MSR spin lock only for those MSRs need bit field writing
    636         //
    637         InitMsrSpinLockByIndex (RegisterTableEntry->Index);
    638       }
    639     }
    640   }
    641 }
    642 
    643 /**
    644   SMM Ready To Lock event notification handler.
    645 
    646   The CPU S3 data is copied to SMRAM for security and mSmmReadyToLock is set to
    647   perform additional lock actions that must be performed from SMM on the next SMI.
    648 
    649   @param[in] Protocol   Points to the protocol's unique identifier.
    650   @param[in] Interface  Points to the interface instance.
    651   @param[in] Handle     The handle on which the interface was installed.
    652 
    653   @retval EFI_SUCCESS   Notification handler runs successfully.
    654  **/
    655 EFI_STATUS
    656 EFIAPI
    657 SmmReadyToLockEventNotify (
    658   IN CONST EFI_GUID  *Protocol,
    659   IN VOID            *Interface,
    660   IN EFI_HANDLE      Handle
    661   )
    662 {
    663   ACPI_CPU_DATA              *AcpiCpuData;
    664   IA32_DESCRIPTOR            *Gdtr;
    665   IA32_DESCRIPTOR            *Idtr;
    666 
    667   //
    668   // Prevent use of mAcpiCpuData by initialize NumberOfCpus to 0
    669   //
    670   mAcpiCpuData.NumberOfCpus = 0;
    671 
    672   //
    673   // If PcdCpuS3DataAddress was never set, then do not copy CPU S3 Data into SMRAM
    674   //
    675   AcpiCpuData = (ACPI_CPU_DATA *)(UINTN)PcdGet64 (PcdCpuS3DataAddress);
    676   if (AcpiCpuData == 0) {
    677     goto Done;
    678   }
    679 
    680   //
    681   // For a native platform, copy the CPU S3 data into SMRAM for use on CPU S3 Resume.
    682   //
    683   CopyMem (&mAcpiCpuData, AcpiCpuData, sizeof (mAcpiCpuData));
    684 
    685   mAcpiCpuData.MtrrTable = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (sizeof (MTRR_SETTINGS));
    686   ASSERT (mAcpiCpuData.MtrrTable != 0);
    687 
    688   CopyMem ((VOID *)(UINTN)mAcpiCpuData.MtrrTable, (VOID *)(UINTN)AcpiCpuData->MtrrTable, sizeof (MTRR_SETTINGS));
    689 
    690   mAcpiCpuData.GdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (sizeof (IA32_DESCRIPTOR));
    691   ASSERT (mAcpiCpuData.GdtrProfile != 0);
    692 
    693   CopyMem ((VOID *)(UINTN)mAcpiCpuData.GdtrProfile, (VOID *)(UINTN)AcpiCpuData->GdtrProfile, sizeof (IA32_DESCRIPTOR));
    694 
    695   mAcpiCpuData.IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (sizeof (IA32_DESCRIPTOR));
    696   ASSERT (mAcpiCpuData.IdtrProfile != 0);
    697 
    698   CopyMem ((VOID *)(UINTN)mAcpiCpuData.IdtrProfile, (VOID *)(UINTN)AcpiCpuData->IdtrProfile, sizeof (IA32_DESCRIPTOR));
    699 
    700   mAcpiCpuData.PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (mAcpiCpuData.NumberOfCpus * sizeof (CPU_REGISTER_TABLE));
    701   ASSERT (mAcpiCpuData.PreSmmInitRegisterTable != 0);
    702 
    703   CopyRegisterTable (
    704     (CPU_REGISTER_TABLE *)(UINTN)mAcpiCpuData.PreSmmInitRegisterTable,
    705     (CPU_REGISTER_TABLE *)(UINTN)AcpiCpuData->PreSmmInitRegisterTable,
    706     mAcpiCpuData.NumberOfCpus
    707     );
    708 
    709   mAcpiCpuData.RegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (mAcpiCpuData.NumberOfCpus * sizeof (CPU_REGISTER_TABLE));
    710   ASSERT (mAcpiCpuData.RegisterTable != 0);
    711 
    712   CopyRegisterTable (
    713     (CPU_REGISTER_TABLE *)(UINTN)mAcpiCpuData.RegisterTable,
    714     (CPU_REGISTER_TABLE *)(UINTN)AcpiCpuData->RegisterTable,
    715     mAcpiCpuData.NumberOfCpus
    716     );
    717 
    718   //
    719   // Copy AP's GDT, IDT and Machine Check handler into SMRAM.
    720   //
    721   Gdtr = (IA32_DESCRIPTOR *)(UINTN)mAcpiCpuData.GdtrProfile;
    722   Idtr = (IA32_DESCRIPTOR *)(UINTN)mAcpiCpuData.IdtrProfile;
    723 
    724   mGdtForAp = AllocatePool ((Gdtr->Limit + 1) + (Idtr->Limit + 1) +  mAcpiCpuData.ApMachineCheckHandlerSize);
    725   ASSERT (mGdtForAp != NULL);
    726   mIdtForAp = (VOID *) ((UINTN)mGdtForAp + (Gdtr->Limit + 1));
    727   mMachineCheckHandlerForAp = (VOID *) ((UINTN)mIdtForAp + (Idtr->Limit + 1));
    728 
    729   CopyMem (mGdtForAp, (VOID *)Gdtr->Base, Gdtr->Limit + 1);
    730   CopyMem (mIdtForAp, (VOID *)Idtr->Base, Idtr->Limit + 1);
    731   CopyMem (mMachineCheckHandlerForAp, (VOID *)(UINTN)mAcpiCpuData.ApMachineCheckHandlerBase, mAcpiCpuData.ApMachineCheckHandlerSize);
    732 
    733 Done:
    734   //
    735   // Set SMM ready to lock flag and return
    736   //
    737   mSmmReadyToLock = TRUE;
    738   return EFI_SUCCESS;
    739 }
    740 
    741 /**
    742   The module Entry Point of the CPU SMM driver.
    743 
    744   @param  ImageHandle    The firmware allocated handle for the EFI image.
    745   @param  SystemTable    A pointer to the EFI System Table.
    746 
    747   @retval EFI_SUCCESS    The entry point is executed successfully.
    748   @retval Other          Some error occurs when executing this entry point.
    749 
    750 **/
    751 EFI_STATUS
    752 EFIAPI
    753 PiCpuSmmEntry (
    754   IN EFI_HANDLE        ImageHandle,
    755   IN EFI_SYSTEM_TABLE  *SystemTable
    756   )
    757 {
    758   EFI_STATUS                 Status;
    759   EFI_MP_SERVICES_PROTOCOL   *MpServices;
    760   UINTN                      NumberOfEnabledProcessors;
    761   UINTN                      Index;
    762   VOID                       *Buffer;
    763   UINTN                      BufferPages;
    764   UINTN                      TileCodeSize;
    765   UINTN                      TileDataSize;
    766   UINTN                      TileSize;
    767   VOID                       *GuidHob;
    768   EFI_SMRAM_DESCRIPTOR       *SmramDescriptor;
    769   SMM_S3_RESUME_STATE        *SmmS3ResumeState;
    770   UINT8                      *Stacks;
    771   VOID                       *Registration;
    772   UINT32                     RegEax;
    773   UINT32                     RegEdx;
    774   UINTN                      FamilyId;
    775   UINTN                      ModelId;
    776   UINT32                     Cr3;
    777 
    778   //
    779   // Initialize Debug Agent to support source level debug in SMM code
    780   //
    781   InitializeDebugAgent (DEBUG_AGENT_INIT_SMM, NULL, NULL);
    782 
    783   //
    784   // Report the start of CPU SMM initialization.
    785   //
    786   REPORT_STATUS_CODE (
    787     EFI_PROGRESS_CODE,
    788     EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_PC_SMM_INIT
    789     );
    790 
    791   //
    792   // Fix segment address of the long-mode-switch jump
    793   //
    794   if (sizeof (UINTN) == sizeof (UINT64)) {
    795     gSmmJmpAddr.Segment = LONG_MODE_CODE_SEGMENT;
    796   }
    797 
    798   //
    799   // Find out SMRR Base and SMRR Size
    800   //
    801   FindSmramInfo (&mCpuHotPlugData.SmrrBase, &mCpuHotPlugData.SmrrSize);
    802 
    803   //
    804   // Get MP Services Protocol
    805   //
    806   Status = SystemTable->BootServices->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpServices);
    807   ASSERT_EFI_ERROR (Status);
    808 
    809   //
    810   // Use MP Services Protocol to retrieve the number of processors and number of enabled processors
    811   //
    812   Status = MpServices->GetNumberOfProcessors (MpServices, &mNumberOfCpus, &NumberOfEnabledProcessors);
    813   ASSERT_EFI_ERROR (Status);
    814   ASSERT (mNumberOfCpus <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
    815 
    816   //
    817   // If support CPU hot plug, PcdCpuSmmEnableBspElection should be set to TRUE.
    818   // A constant BSP index makes no sense because it may be hot removed.
    819   //
    820   DEBUG_CODE (
    821     if (FeaturePcdGet (PcdCpuHotPlugSupport)) {
    822 
    823       ASSERT (FeaturePcdGet (PcdCpuSmmEnableBspElection));
    824     }
    825   );
    826 
    827   //
    828   // Save the PcdCpuSmmCodeAccessCheckEnable value into a global variable.
    829   //
    830   mSmmCodeAccessCheckEnable = PcdGetBool (PcdCpuSmmCodeAccessCheckEnable);
    831   DEBUG ((EFI_D_INFO, "PcdCpuSmmCodeAccessCheckEnable = %d\n", mSmmCodeAccessCheckEnable));
    832 
    833   //
    834   // If support CPU hot plug, we need to allocate resources for possibly hot-added processors
    835   //
    836   if (FeaturePcdGet (PcdCpuHotPlugSupport)) {
    837     mMaxNumberOfCpus = PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
    838   } else {
    839     mMaxNumberOfCpus = mNumberOfCpus;
    840   }
    841   gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus = mMaxNumberOfCpus;
    842 
    843   //
    844   // The CPU save state and code for the SMI entry point are tiled within an SMRAM
    845   // allocated buffer.  The minimum size of this buffer for a uniprocessor system
    846   // is 32 KB, because the entry point is SMBASE + 32KB, and CPU save state area
    847   // just below SMBASE + 64KB.  If more than one CPU is present in the platform,
    848   // then the SMI entry point and the CPU save state areas can be tiles to minimize
    849   // the total amount SMRAM required for all the CPUs.  The tile size can be computed
    850   // by adding the   // CPU save state size, any extra CPU specific context, and
    851   // the size of code that must be placed at the SMI entry point to transfer
    852   // control to a C function in the native SMM execution mode.  This size is
    853   // rounded up to the nearest power of 2 to give the tile size for a each CPU.
    854   // The total amount of memory required is the maximum number of CPUs that
    855   // platform supports times the tile size.  The picture below shows the tiling,
    856   // where m is the number of tiles that fit in 32KB.
    857   //
    858   //  +-----------------------------+  <-- 2^n offset from Base of allocated buffer
    859   //  |   CPU m+1 Save State        |
    860   //  +-----------------------------+
    861   //  |   CPU m+1 Extra Data        |
    862   //  +-----------------------------+
    863   //  |   Padding                   |
    864   //  +-----------------------------+
    865   //  |   CPU 2m  SMI Entry         |
    866   //  +#############################+  <-- Base of allocated buffer + 64 KB
    867   //  |   CPU m-1 Save State        |
    868   //  +-----------------------------+
    869   //  |   CPU m-1 Extra Data        |
    870   //  +-----------------------------+
    871   //  |   Padding                   |
    872   //  +-----------------------------+
    873   //  |   CPU 2m-1 SMI Entry        |
    874   //  +=============================+  <-- 2^n offset from Base of allocated buffer
    875   //  |   . . . . . . . . . . . .   |
    876   //  +=============================+  <-- 2^n offset from Base of allocated buffer
    877   //  |   CPU 2 Save State          |
    878   //  +-----------------------------+
    879   //  |   CPU 2 Extra Data          |
    880   //  +-----------------------------+
    881   //  |   Padding                   |
    882   //  +-----------------------------+
    883   //  |   CPU m+1 SMI Entry         |
    884   //  +=============================+  <-- Base of allocated buffer + 32 KB
    885   //  |   CPU 1 Save State          |
    886   //  +-----------------------------+
    887   //  |   CPU 1 Extra Data          |
    888   //  +-----------------------------+
    889   //  |   Padding                   |
    890   //  +-----------------------------+
    891   //  |   CPU m SMI Entry           |
    892   //  +#############################+  <-- Base of allocated buffer + 32 KB == CPU 0 SMBASE + 64 KB
    893   //  |   CPU 0 Save State          |
    894   //  +-----------------------------+
    895   //  |   CPU 0 Extra Data          |
    896   //  +-----------------------------+
    897   //  |   Padding                   |
    898   //  +-----------------------------+
    899   //  |   CPU m-1 SMI Entry         |
    900   //  +=============================+  <-- 2^n offset from Base of allocated buffer
    901   //  |   . . . . . . . . . . . .   |
    902   //  +=============================+  <-- 2^n offset from Base of allocated buffer
    903   //  |   Padding                   |
    904   //  +-----------------------------+
    905   //  |   CPU 1 SMI Entry           |
    906   //  +=============================+  <-- 2^n offset from Base of allocated buffer
    907   //  |   Padding                   |
    908   //  +-----------------------------+
    909   //  |   CPU 0 SMI Entry           |
    910   //  +#############################+  <-- Base of allocated buffer == CPU 0 SMBASE + 32 KB
    911   //
    912 
    913   //
    914   // Retrieve CPU Family
    915   //
    916   AsmCpuid (CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL);
    917   FamilyId = (RegEax >> 8) & 0xf;
    918   ModelId = (RegEax >> 4) & 0xf;
    919   if (FamilyId == 0x06 || FamilyId == 0x0f) {
    920     ModelId = ModelId | ((RegEax >> 12) & 0xf0);
    921   }
    922 
    923   RegEdx = 0;
    924   AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
    925   if (RegEax >= CPUID_EXTENDED_CPU_SIG) {
    926     AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx);
    927   }
    928   //
    929   // Determine the mode of the CPU at the time an SMI occurs
    930   //   Intel(R) 64 and IA-32 Architectures Software Developer's Manual
    931   //   Volume 3C, Section 34.4.1.1
    932   //
    933   mSmmSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT;
    934   if ((RegEdx & BIT29) != 0) {
    935     mSmmSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_64BIT;
    936   }
    937   if (FamilyId == 0x06) {
    938     if (ModelId == 0x17 || ModelId == 0x0f || ModelId == 0x1c) {
    939       mSmmSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_64BIT;
    940     }
    941   }
    942 
    943   //
    944   // Compute tile size of buffer required to hold the CPU SMRAM Save State Map, extra CPU
    945   // specific context in a PROCESSOR_SMM_DESCRIPTOR, and the SMI entry point.  This size
    946   // is rounded up to nearest power of 2.
    947   //
    948   TileCodeSize = GetSmiHandlerSize ();
    949   TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB);
    950   TileDataSize = sizeof (SMRAM_SAVE_STATE_MAP) + sizeof (PROCESSOR_SMM_DESCRIPTOR);
    951   TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB);
    952   TileSize = TileDataSize + TileCodeSize - 1;
    953   TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);
    954   DEBUG ((EFI_D_INFO, "SMRAM TileSize = 0x%08x (0x%08x, 0x%08x)\n", TileSize, TileCodeSize, TileDataSize));
    955 
    956   //
    957   // If the TileSize is larger than space available for the SMI Handler of CPU[i],
    958   // the PROCESSOR_SMM_DESCRIPTOR of CPU[i+1] and the SMRAM Save State Map of CPU[i+1],
    959   // the ASSERT().  If this ASSERT() is triggered, then the SMI Handler size must be
    960   // reduced.
    961   //
    962   ASSERT (TileSize <= (SMRAM_SAVE_STATE_MAP_OFFSET + sizeof (SMRAM_SAVE_STATE_MAP) - SMM_HANDLER_OFFSET));
    963 
    964   //
    965   // Allocate buffer for all of the tiles.
    966   //
    967   // Intel(R) 64 and IA-32 Architectures Software Developer's Manual
    968   // Volume 3C, Section 34.11 SMBASE Relocation
    969   //   For Pentium and Intel486 processors, the SMBASE values must be
    970   //   aligned on a 32-KByte boundary or the processor will enter shutdown
    971   //   state during the execution of a RSM instruction.
    972   //
    973   // Intel486 processors: FamilyId is 4
    974   // Pentium processors : FamilyId is 5
    975   //
    976   BufferPages = EFI_SIZE_TO_PAGES (SIZE_32KB + TileSize * (mMaxNumberOfCpus - 1));
    977   if ((FamilyId == 4) || (FamilyId == 5)) {
    978     Buffer = AllocateAlignedPages (BufferPages, SIZE_32KB);
    979   } else {
    980     Buffer = AllocateAlignedPages (BufferPages, SIZE_4KB);
    981   }
    982   ASSERT (Buffer != NULL);
    983   DEBUG ((EFI_D_INFO, "SMRAM SaveState Buffer (0x%08x, 0x%08x)\n", Buffer, EFI_PAGES_TO_SIZE(BufferPages)));
    984 
    985   //
    986   // Allocate buffer for pointers to array in  SMM_CPU_PRIVATE_DATA.
    987   //
    988   gSmmCpuPrivate->ProcessorInfo = (EFI_PROCESSOR_INFORMATION *)AllocatePool (sizeof (EFI_PROCESSOR_INFORMATION) * mMaxNumberOfCpus);
    989   ASSERT (gSmmCpuPrivate->ProcessorInfo != NULL);
    990 
    991   gSmmCpuPrivate->Operation = (SMM_CPU_OPERATION *)AllocatePool (sizeof (SMM_CPU_OPERATION) * mMaxNumberOfCpus);
    992   ASSERT (gSmmCpuPrivate->Operation != NULL);
    993 
    994   gSmmCpuPrivate->CpuSaveStateSize = (UINTN *)AllocatePool (sizeof (UINTN) * mMaxNumberOfCpus);
    995   ASSERT (gSmmCpuPrivate->CpuSaveStateSize != NULL);
    996 
    997   gSmmCpuPrivate->CpuSaveState = (VOID **)AllocatePool (sizeof (VOID *) * mMaxNumberOfCpus);
    998   ASSERT (gSmmCpuPrivate->CpuSaveState != NULL);
    999 
   1000   mSmmCpuPrivateData.SmmCoreEntryContext.CpuSaveStateSize = gSmmCpuPrivate->CpuSaveStateSize;
   1001   mSmmCpuPrivateData.SmmCoreEntryContext.CpuSaveState     = gSmmCpuPrivate->CpuSaveState;
   1002 
   1003   //
   1004   // Allocate buffer for pointers to array in CPU_HOT_PLUG_DATA.
   1005   //
   1006   mCpuHotPlugData.ApicId = (UINT64 *)AllocatePool (sizeof (UINT64) * mMaxNumberOfCpus);
   1007   ASSERT (mCpuHotPlugData.ApicId != NULL);
   1008   mCpuHotPlugData.SmBase = (UINTN *)AllocatePool (sizeof (UINTN) * mMaxNumberOfCpus);
   1009   ASSERT (mCpuHotPlugData.SmBase != NULL);
   1010   mCpuHotPlugData.ArrayLength = (UINT32)mMaxNumberOfCpus;
   1011 
   1012   //
   1013   // Retrieve APIC ID of each enabled processor from the MP Services protocol.
   1014   // Also compute the SMBASE address, CPU Save State address, and CPU Save state
   1015   // size for each CPU in the platform
   1016   //
   1017   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
   1018     mCpuHotPlugData.SmBase[Index]          = (UINTN)Buffer + Index * TileSize - SMM_HANDLER_OFFSET;
   1019     gSmmCpuPrivate->CpuSaveStateSize[Index] = sizeof(SMRAM_SAVE_STATE_MAP);
   1020     gSmmCpuPrivate->CpuSaveState[Index]     = (VOID *)(mCpuHotPlugData.SmBase[Index] + SMRAM_SAVE_STATE_MAP_OFFSET);
   1021     gSmmCpuPrivate->Operation[Index] = SmmCpuNone;
   1022 
   1023     if (Index < mNumberOfCpus) {
   1024       Status = MpServices->GetProcessorInfo (MpServices, Index, &gSmmCpuPrivate->ProcessorInfo[Index]);
   1025       ASSERT_EFI_ERROR (Status);
   1026       mCpuHotPlugData.ApicId[Index] = gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId;
   1027 
   1028       DEBUG ((EFI_D_INFO, "CPU[%03x]  APIC ID=%04x  SMBASE=%08x  SaveState=%08x  Size=%08x\n",
   1029         Index,
   1030         (UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId,
   1031         mCpuHotPlugData.SmBase[Index],
   1032         gSmmCpuPrivate->CpuSaveState[Index],
   1033         gSmmCpuPrivate->CpuSaveStateSize[Index]
   1034         ));
   1035     } else {
   1036       gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId = INVALID_APIC_ID;
   1037       mCpuHotPlugData.ApicId[Index] = INVALID_APIC_ID;
   1038     }
   1039   }
   1040 
   1041   //
   1042   // Allocate SMI stacks for all processors.
   1043   //
   1044   if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
   1045     //
   1046     // 2 more pages is allocated for each processor.
   1047     // one is guard page and the other is known good stack.
   1048     //
   1049     // +-------------------------------------------+-----+-------------------------------------------+
   1050     // | Known Good Stack | Guard Page | SMM Stack | ... | Known Good Stack | Guard Page | SMM Stack |
   1051     // +-------------------------------------------+-----+-------------------------------------------+
   1052     // |                                           |     |                                           |
   1053     // |<-------------- Processor 0 -------------->|     |<-------------- Processor n -------------->|
   1054     //
   1055     mSmmStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStackSize)) + 2);
   1056     Stacks = (UINT8 *) AllocatePages (gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus * (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStackSize)) + 2));
   1057     ASSERT (Stacks != NULL);
   1058     mSmmStackArrayBase = (UINTN)Stacks;
   1059     mSmmStackArrayEnd = mSmmStackArrayBase + gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus * mSmmStackSize - 1;
   1060   } else {
   1061     mSmmStackSize = PcdGet32 (PcdCpuSmmStackSize);
   1062     Stacks = (UINT8 *) AllocatePages (EFI_SIZE_TO_PAGES (gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus * mSmmStackSize));
   1063     ASSERT (Stacks != NULL);
   1064   }
   1065 
   1066   //
   1067   // Set SMI stack for SMM base relocation
   1068   //
   1069   gSmmInitStack = (UINTN) (Stacks + mSmmStackSize - sizeof (UINTN));
   1070 
   1071   //
   1072   // Initialize IDT
   1073   //
   1074   InitializeSmmIdt ();
   1075 
   1076   //
   1077   // Relocate SMM Base addresses to the ones allocated from SMRAM
   1078   //
   1079   mRebased = (BOOLEAN *)AllocateZeroPool (sizeof (BOOLEAN) * mMaxNumberOfCpus);
   1080   ASSERT (mRebased != NULL);
   1081   SmmRelocateBases ();
   1082 
   1083   //
   1084   // Call hook for BSP to perform extra actions in normal mode after all
   1085   // SMM base addresses have been relocated on all CPUs
   1086   //
   1087   SmmCpuFeaturesSmmRelocationComplete ();
   1088 
   1089   //
   1090   // SMM Time initialization
   1091   //
   1092   InitializeSmmTimer ();
   1093 
   1094   //
   1095   // Initialize MP globals
   1096   //
   1097   Cr3 = InitializeMpServiceData (Stacks, mSmmStackSize);
   1098 
   1099   //
   1100   // Fill in SMM Reserved Regions
   1101   //
   1102   gSmmCpuPrivate->SmmReservedSmramRegion[0].SmramReservedStart = 0;
   1103   gSmmCpuPrivate->SmmReservedSmramRegion[0].SmramReservedSize  = 0;
   1104 
   1105   //
   1106   // Install the SMM Configuration Protocol onto a new handle on the handle database.
   1107   // The entire SMM Configuration Protocol is allocated from SMRAM, so only a pointer
   1108   // to an SMRAM address will be present in the handle database
   1109   //
   1110   Status = SystemTable->BootServices->InstallMultipleProtocolInterfaces (
   1111                                         &gSmmCpuPrivate->SmmCpuHandle,
   1112                                         &gEfiSmmConfigurationProtocolGuid, &gSmmCpuPrivate->SmmConfiguration,
   1113                                         NULL
   1114                                         );
   1115   ASSERT_EFI_ERROR (Status);
   1116 
   1117   //
   1118   // Install the SMM CPU Protocol into SMM protocol database
   1119   //
   1120   Status = gSmst->SmmInstallProtocolInterface (
   1121                     &mSmmCpuHandle,
   1122                     &gEfiSmmCpuProtocolGuid,
   1123                     EFI_NATIVE_INTERFACE,
   1124                     &mSmmCpu
   1125                     );
   1126   ASSERT_EFI_ERROR (Status);
   1127 
   1128   //
   1129   // Expose address of CPU Hot Plug Data structure if CPU hot plug is supported.
   1130   //
   1131   if (FeaturePcdGet (PcdCpuHotPlugSupport)) {
   1132     Status = PcdSet64S (PcdCpuHotPlugDataAddress, (UINT64)(UINTN)&mCpuHotPlugData);
   1133     ASSERT_EFI_ERROR (Status);
   1134   }
   1135 
   1136   //
   1137   // Initialize SMM CPU Services Support
   1138   //
   1139   Status = InitializeSmmCpuServices (mSmmCpuHandle);
   1140   ASSERT_EFI_ERROR (Status);
   1141 
   1142   //
   1143   // register SMM Ready To Lock Protocol notification
   1144   //
   1145   Status = gSmst->SmmRegisterProtocolNotify (
   1146                     &gEfiSmmReadyToLockProtocolGuid,
   1147                     SmmReadyToLockEventNotify,
   1148                     &Registration
   1149                     );
   1150   ASSERT_EFI_ERROR (Status);
   1151 
   1152   GuidHob = GetFirstGuidHob (&gEfiAcpiVariableGuid);
   1153   if (GuidHob != NULL) {
   1154     SmramDescriptor = (EFI_SMRAM_DESCRIPTOR *) GET_GUID_HOB_DATA (GuidHob);
   1155 
   1156     DEBUG ((EFI_D_INFO, "SMM S3 SMRAM Structure = %x\n", SmramDescriptor));
   1157     DEBUG ((EFI_D_INFO, "SMM S3 Structure = %x\n", SmramDescriptor->CpuStart));
   1158 
   1159     SmmS3ResumeState = (SMM_S3_RESUME_STATE *)(UINTN)SmramDescriptor->CpuStart;
   1160     ZeroMem (SmmS3ResumeState, sizeof (SMM_S3_RESUME_STATE));
   1161 
   1162     mSmmS3ResumeState = SmmS3ResumeState;
   1163     SmmS3ResumeState->Smst = (EFI_PHYSICAL_ADDRESS)(UINTN)gSmst;
   1164 
   1165     SmmS3ResumeState->SmmS3ResumeEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)SmmRestoreCpu;
   1166 
   1167     SmmS3ResumeState->SmmS3StackSize = SIZE_32KB;
   1168     SmmS3ResumeState->SmmS3StackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)SmmS3ResumeState->SmmS3StackSize));
   1169     if (SmmS3ResumeState->SmmS3StackBase == 0) {
   1170       SmmS3ResumeState->SmmS3StackSize = 0;
   1171     }
   1172 
   1173     SmmS3ResumeState->SmmS3Cr0 = gSmmCr0;
   1174     SmmS3ResumeState->SmmS3Cr3 = Cr3;
   1175     SmmS3ResumeState->SmmS3Cr4 = gSmmCr4;
   1176 
   1177     if (sizeof (UINTN) == sizeof (UINT64)) {
   1178       SmmS3ResumeState->Signature = SMM_S3_RESUME_SMM_64;
   1179     }
   1180     if (sizeof (UINTN) == sizeof (UINT32)) {
   1181       SmmS3ResumeState->Signature = SMM_S3_RESUME_SMM_32;
   1182     }
   1183   }
   1184 
   1185   //
   1186   // Check XD and BTS features
   1187   //
   1188   CheckProcessorFeature ();
   1189 
   1190   //
   1191   // Initialize SMM Profile feature
   1192   //
   1193   InitSmmProfile (Cr3);
   1194 
   1195   //
   1196   // Patch SmmS3ResumeState->SmmS3Cr3
   1197   //
   1198   InitSmmS3Cr3 ();
   1199 
   1200   DEBUG ((EFI_D_INFO, "SMM CPU Module exit from SMRAM with EFI_SUCCESS\n"));
   1201 
   1202   return EFI_SUCCESS;
   1203 }
   1204 
   1205 /**
   1206 
   1207   Find out SMRAM information including SMRR base and SMRR size.
   1208 
   1209   @param          SmrrBase          SMRR base
   1210   @param          SmrrSize          SMRR size
   1211 
   1212 **/
   1213 VOID
   1214 FindSmramInfo (
   1215   OUT UINT32   *SmrrBase,
   1216   OUT UINT32   *SmrrSize
   1217   )
   1218 {
   1219   EFI_STATUS                        Status;
   1220   UINTN                             Size;
   1221   EFI_SMM_ACCESS2_PROTOCOL          *SmmAccess;
   1222   EFI_SMRAM_DESCRIPTOR              *CurrentSmramRange;
   1223   EFI_SMRAM_DESCRIPTOR              *SmramRanges;
   1224   UINTN                             SmramRangeCount;
   1225   UINTN                             Index;
   1226   UINT64                            MaxSize;
   1227   BOOLEAN                           Found;
   1228 
   1229   //
   1230   // Get SMM Access Protocol
   1231   //
   1232   Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess);
   1233   ASSERT_EFI_ERROR (Status);
   1234 
   1235   //
   1236   // Get SMRAM information
   1237   //
   1238   Size = 0;
   1239   Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);
   1240   ASSERT (Status == EFI_BUFFER_TOO_SMALL);
   1241 
   1242   SmramRanges = (EFI_SMRAM_DESCRIPTOR *)AllocatePool (Size);
   1243   ASSERT (SmramRanges != NULL);
   1244 
   1245   Status = SmmAccess->GetCapabilities (SmmAccess, &Size, SmramRanges);
   1246   ASSERT_EFI_ERROR (Status);
   1247 
   1248   SmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
   1249 
   1250   //
   1251   // Find the largest SMRAM range between 1MB and 4GB that is at least 256K - 4K in size
   1252   //
   1253   CurrentSmramRange = NULL;
   1254   for (Index = 0, MaxSize = SIZE_256KB - EFI_PAGE_SIZE; Index < SmramRangeCount; Index++) {
   1255     //
   1256     // Skip any SMRAM region that is already allocated, needs testing, or needs ECC initialization
   1257     //
   1258     if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
   1259       continue;
   1260     }
   1261 
   1262     if (SmramRanges[Index].CpuStart >= BASE_1MB) {
   1263       if ((SmramRanges[Index].CpuStart + SmramRanges[Index].PhysicalSize) <= BASE_4GB) {
   1264         if (SmramRanges[Index].PhysicalSize >= MaxSize) {
   1265           MaxSize = SmramRanges[Index].PhysicalSize;
   1266           CurrentSmramRange = &SmramRanges[Index];
   1267         }
   1268       }
   1269     }
   1270   }
   1271 
   1272   ASSERT (CurrentSmramRange != NULL);
   1273 
   1274   *SmrrBase = (UINT32)CurrentSmramRange->CpuStart;
   1275   *SmrrSize = (UINT32)CurrentSmramRange->PhysicalSize;
   1276 
   1277   do {
   1278     Found = FALSE;
   1279     for (Index = 0; Index < SmramRangeCount; Index++) {
   1280       if (SmramRanges[Index].CpuStart < *SmrrBase && *SmrrBase == (SmramRanges[Index].CpuStart + SmramRanges[Index].PhysicalSize)) {
   1281         *SmrrBase = (UINT32)SmramRanges[Index].CpuStart;
   1282         *SmrrSize = (UINT32)(*SmrrSize + SmramRanges[Index].PhysicalSize);
   1283         Found = TRUE;
   1284       } else if ((*SmrrBase + *SmrrSize) == SmramRanges[Index].CpuStart && SmramRanges[Index].PhysicalSize > 0) {
   1285         *SmrrSize = (UINT32)(*SmrrSize + SmramRanges[Index].PhysicalSize);
   1286         Found = TRUE;
   1287       }
   1288     }
   1289   } while (Found);
   1290 
   1291   DEBUG ((EFI_D_INFO, "SMRR Base: 0x%x, SMRR Size: 0x%x\n", *SmrrBase, *SmrrSize));
   1292 }
   1293 
   1294 /**
   1295 Configure SMM Code Access Check feature on an AP.
   1296 SMM Feature Control MSR will be locked after configuration.
   1297 
   1298 @param[in,out] Buffer  Pointer to private data buffer.
   1299 **/
   1300 VOID
   1301 EFIAPI
   1302 ConfigSmmCodeAccessCheckOnCurrentProcessor (
   1303   IN OUT VOID  *Buffer
   1304   )
   1305 {
   1306   UINTN   CpuIndex;
   1307   UINT64  SmmFeatureControlMsr;
   1308   UINT64  NewSmmFeatureControlMsr;
   1309 
   1310   //
   1311   // Retrieve the CPU Index from the context passed in
   1312   //
   1313   CpuIndex = *(UINTN *)Buffer;
   1314 
   1315   //
   1316   // Get the current SMM Feature Control MSR value
   1317   //
   1318   SmmFeatureControlMsr = SmmCpuFeaturesGetSmmRegister (CpuIndex, SmmRegFeatureControl);
   1319 
   1320   //
   1321   // Compute the new SMM Feature Control MSR value
   1322   //
   1323   NewSmmFeatureControlMsr = SmmFeatureControlMsr;
   1324   if (mSmmCodeAccessCheckEnable) {
   1325     NewSmmFeatureControlMsr |= SMM_CODE_CHK_EN_BIT;
   1326     if (FeaturePcdGet (PcdCpuSmmFeatureControlMsrLock)) {
   1327       NewSmmFeatureControlMsr |= SMM_FEATURE_CONTROL_LOCK_BIT;
   1328     }
   1329   }
   1330 
   1331   //
   1332   // Only set the SMM Feature Control MSR value if the new value is different than the current value
   1333   //
   1334   if (NewSmmFeatureControlMsr != SmmFeatureControlMsr) {
   1335     SmmCpuFeaturesSetSmmRegister (CpuIndex, SmmRegFeatureControl, NewSmmFeatureControlMsr);
   1336   }
   1337 
   1338   //
   1339   // Release the spin lock user to serialize the updates to the SMM Feature Control MSR
   1340   //
   1341   ReleaseSpinLock (&mConfigSmmCodeAccessCheckLock);
   1342 }
   1343 
   1344 /**
   1345 Configure SMM Code Access Check feature for all processors.
   1346 SMM Feature Control MSR will be locked after configuration.
   1347 **/
   1348 VOID
   1349 ConfigSmmCodeAccessCheck (
   1350   VOID
   1351   )
   1352 {
   1353   UINTN       Index;
   1354   EFI_STATUS  Status;
   1355 
   1356   //
   1357   // Check to see if the Feature Control MSR is supported on this CPU
   1358   //
   1359   Index = gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu;
   1360   if (!SmmCpuFeaturesIsSmmRegisterSupported (Index, SmmRegFeatureControl)) {
   1361     mSmmCodeAccessCheckEnable = FALSE;
   1362     return;
   1363   }
   1364 
   1365   //
   1366   // Check to see if the CPU supports the SMM Code Access Check feature
   1367   // Do not access this MSR unless the CPU supports the SmmRegFeatureControl
   1368   //
   1369   if ((AsmReadMsr64 (EFI_MSR_SMM_MCA_CAP) & SMM_CODE_ACCESS_CHK_BIT) == 0) {
   1370     mSmmCodeAccessCheckEnable = FALSE;
   1371     return;
   1372   }
   1373 
   1374   //
   1375   // Initialize the lock used to serialize the MSR programming in BSP and all APs
   1376   //
   1377   InitializeSpinLock (&mConfigSmmCodeAccessCheckLock);
   1378 
   1379   //
   1380   // Acquire Config SMM Code Access Check spin lock.  The BSP will release the
   1381   // spin lock when it is done executing ConfigSmmCodeAccessCheckOnCurrentProcessor().
   1382   //
   1383   AcquireSpinLock (&mConfigSmmCodeAccessCheckLock);
   1384 
   1385   //
   1386   // Enable SMM Code Access Check feature on the BSP.
   1387   //
   1388   ConfigSmmCodeAccessCheckOnCurrentProcessor (&Index);
   1389 
   1390   //
   1391   // Enable SMM Code Access Check feature for the APs.
   1392   //
   1393   for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
   1394     if (Index != gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) {
   1395 
   1396       //
   1397       // Acquire Config SMM Code Access Check spin lock.  The AP will release the
   1398       // spin lock when it is done executing ConfigSmmCodeAccessCheckOnCurrentProcessor().
   1399       //
   1400       AcquireSpinLock (&mConfigSmmCodeAccessCheckLock);
   1401 
   1402       //
   1403       // Call SmmStartupThisAp() to enable SMM Code Access Check on an AP.
   1404       //
   1405       Status = gSmst->SmmStartupThisAp (ConfigSmmCodeAccessCheckOnCurrentProcessor, Index, &Index);
   1406       ASSERT_EFI_ERROR (Status);
   1407 
   1408       //
   1409       // Wait for the AP to release the Config SMM Code Access Check spin lock.
   1410       //
   1411       while (!AcquireSpinLockOrFail (&mConfigSmmCodeAccessCheckLock)) {
   1412         CpuPause ();
   1413       }
   1414 
   1415       //
   1416       // Release the Config SMM Code Access Check spin lock.
   1417       //
   1418       ReleaseSpinLock (&mConfigSmmCodeAccessCheckLock);
   1419     }
   1420   }
   1421 }
   1422 
   1423 /**
   1424   This API provides a way to allocate memory for page table.
   1425 
   1426   This API can be called more once to allocate memory for page tables.
   1427 
   1428   Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the
   1429   allocated buffer.  The buffer returned is aligned on a 4KB boundary.  If Pages is 0, then NULL
   1430   is returned.  If there is not enough memory remaining to satisfy the request, then NULL is
   1431   returned.
   1432 
   1433   @param  Pages                 The number of 4 KB pages to allocate.
   1434 
   1435   @return A pointer to the allocated buffer or NULL if allocation fails.
   1436 
   1437 **/
   1438 VOID *
   1439 AllocatePageTableMemory (
   1440   IN UINTN           Pages
   1441   )
   1442 {
   1443   VOID  *Buffer;
   1444 
   1445   Buffer = SmmCpuFeaturesAllocatePageTableMemory (Pages);
   1446   if (Buffer != NULL) {
   1447     return Buffer;
   1448   }
   1449   return AllocatePages (Pages);
   1450 }
   1451 
   1452 /**
   1453   Perform the remaining tasks.
   1454 
   1455 **/
   1456 VOID
   1457 PerformRemainingTasks (
   1458   VOID
   1459   )
   1460 {
   1461   if (mSmmReadyToLock) {
   1462     //
   1463     // Start SMM Profile feature
   1464     //
   1465     if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
   1466       SmmProfileStart ();
   1467     }
   1468     //
   1469     // Create a mix of 2MB and 4KB page table. Update some memory ranges absent and execute-disable.
   1470     //
   1471     InitPaging ();
   1472     //
   1473     // Configure SMM Code Access Check feature if available.
   1474     //
   1475     ConfigSmmCodeAccessCheck ();
   1476 
   1477     SmmCpuFeaturesCompleteSmmReadyToLock ();
   1478 
   1479     //
   1480     // Clean SMM ready to lock flag
   1481     //
   1482     mSmmReadyToLock = FALSE;
   1483   }
   1484 }
   1485 
   1486 /**
   1487   Perform the pre tasks.
   1488 
   1489 **/
   1490 VOID
   1491 PerformPreTasks (
   1492   VOID
   1493   )
   1494 {
   1495   //
   1496   // Restore SMM Configuration in S3 boot path.
   1497   //
   1498   if (mRestoreSmmConfigurationInS3) {
   1499     //
   1500     // Need make sure gSmst is correct because below function may use them.
   1501     //
   1502     gSmst->SmmStartupThisAp      = gSmmCpuPrivate->SmmCoreEntryContext.SmmStartupThisAp;
   1503     gSmst->CurrentlyExecutingCpu = gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu;
   1504     gSmst->NumberOfCpus          = gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus;
   1505     gSmst->CpuSaveStateSize      = gSmmCpuPrivate->SmmCoreEntryContext.CpuSaveStateSize;
   1506     gSmst->CpuSaveState          = gSmmCpuPrivate->SmmCoreEntryContext.CpuSaveState;
   1507 
   1508     //
   1509     // Configure SMM Code Access Check feature if available.
   1510     //
   1511     ConfigSmmCodeAccessCheck ();
   1512 
   1513     SmmCpuFeaturesCompleteSmmReadyToLock ();
   1514 
   1515     mRestoreSmmConfigurationInS3 = FALSE;
   1516   }
   1517 }
   1518