Home | History | Annotate | Download | only in SmmCpuFeaturesLib
      1 /** @file
      2 The CPU specific programming for PiSmmCpuDxeSmm module.
      3 
      4 Copyright (c) 2010 - 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 <PiSmm.h>
     16 #include <Library/SmmCpuFeaturesLib.h>
     17 #include <Library/BaseLib.h>
     18 #include <Library/BaseMemoryLib.h>
     19 #include <Library/PcdLib.h>
     20 #include <Library/MemoryAllocationLib.h>
     21 #include <Library/SmmServicesTableLib.h>
     22 #include <Library/DebugLib.h>
     23 #include <Register/QemuSmramSaveStateMap.h>
     24 
     25 //
     26 // EFER register LMA bit
     27 //
     28 #define LMA BIT10
     29 
     30 /**
     31   The constructor function
     32 
     33   @param[in]  ImageHandle  The firmware allocated handle for the EFI image.
     34   @param[in]  SystemTable  A pointer to the EFI System Table.
     35 
     36   @retval EFI_SUCCESS      The constructor always returns EFI_SUCCESS.
     37 
     38 **/
     39 EFI_STATUS
     40 EFIAPI
     41 SmmCpuFeaturesLibConstructor (
     42   IN EFI_HANDLE        ImageHandle,
     43   IN EFI_SYSTEM_TABLE  *SystemTable
     44   )
     45 {
     46   //
     47   // No need to program SMRRs on our virtual platform.
     48   //
     49   return EFI_SUCCESS;
     50 }
     51 
     52 /**
     53   Called during the very first SMI into System Management Mode to initialize
     54   CPU features, including SMBASE, for the currently executing CPU.  Since this
     55   is the first SMI, the SMRAM Save State Map is at the default address of
     56   SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET.  The currently executing
     57   CPU is specified by CpuIndex and CpuIndex can be used to access information
     58   about the currently executing CPU in the ProcessorInfo array and the
     59   HotPlugCpuData data structure.
     60 
     61   @param[in] CpuIndex        The index of the CPU to initialize.  The value
     62                              must be between 0 and the NumberOfCpus field in
     63                              the System Management System Table (SMST).
     64   @param[in] IsMonarch       TRUE if the CpuIndex is the index of the CPU that
     65                              was elected as monarch during System Management
     66                              Mode initialization.
     67                              FALSE if the CpuIndex is not the index of the CPU
     68                              that was elected as monarch during System
     69                              Management Mode initialization.
     70   @param[in] ProcessorInfo   Pointer to an array of EFI_PROCESSOR_INFORMATION
     71                              structures.  ProcessorInfo[CpuIndex] contains the
     72                              information for the currently executing CPU.
     73   @param[in] CpuHotPlugData  Pointer to the CPU_HOT_PLUG_DATA structure that
     74                              contains the ApidId and SmBase arrays.
     75 **/
     76 VOID
     77 EFIAPI
     78 SmmCpuFeaturesInitializeProcessor (
     79   IN UINTN                      CpuIndex,
     80   IN BOOLEAN                    IsMonarch,
     81   IN EFI_PROCESSOR_INFORMATION  *ProcessorInfo,
     82   IN CPU_HOT_PLUG_DATA          *CpuHotPlugData
     83   )
     84 {
     85   QEMU_SMRAM_SAVE_STATE_MAP  *CpuState;
     86 
     87   //
     88   // Configure SMBASE.
     89   //
     90   CpuState = (QEMU_SMRAM_SAVE_STATE_MAP *)(UINTN)(SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET);
     91   if ((CpuState->x86.SMMRevId & 0xFFFF) == 0) {
     92     CpuState->x86.SMBASE = (UINT32)CpuHotPlugData->SmBase[CpuIndex];
     93   } else {
     94     CpuState->x64.SMBASE = (UINT32)CpuHotPlugData->SmBase[CpuIndex];
     95   }
     96 
     97   //
     98   // No need to program SMRRs on our virtual platform.
     99   //
    100 }
    101 
    102 /**
    103   This function updates the SMRAM save state on the currently executing CPU
    104   to resume execution at a specific address after an RSM instruction.  This
    105   function must evaluate the SMRAM save state to determine the execution mode
    106   the RSM instruction resumes and update the resume execution address with
    107   either NewInstructionPointer32 or NewInstructionPoint.  The auto HALT restart
    108   flag in the SMRAM save state must always be cleared.  This function returns
    109   the value of the instruction pointer from the SMRAM save state that was
    110   replaced.  If this function returns 0, then the SMRAM save state was not
    111   modified.
    112 
    113   This function is called during the very first SMI on each CPU after
    114   SmmCpuFeaturesInitializeProcessor() to set a flag in normal execution mode
    115   to signal that the SMBASE of each CPU has been updated before the default
    116   SMBASE address is used for the first SMI to the next CPU.
    117 
    118   @param[in] CpuIndex                 The index of the CPU to hook.  The value
    119                                       must be between 0 and the NumberOfCpus
    120                                       field in the System Management System Table
    121                                       (SMST).
    122   @param[in] CpuState                 Pointer to SMRAM Save State Map for the
    123                                       currently executing CPU.
    124   @param[in] NewInstructionPointer32  Instruction pointer to use if resuming to
    125                                       32-bit execution mode from 64-bit SMM.
    126   @param[in] NewInstructionPointer    Instruction pointer to use if resuming to
    127                                       same execution mode as SMM.
    128 
    129   @retval 0    This function did modify the SMRAM save state.
    130   @retval > 0  The original instruction pointer value from the SMRAM save state
    131                before it was replaced.
    132 **/
    133 UINT64
    134 EFIAPI
    135 SmmCpuFeaturesHookReturnFromSmm (
    136   IN UINTN                 CpuIndex,
    137   IN SMRAM_SAVE_STATE_MAP  *CpuState,
    138   IN UINT64                NewInstructionPointer32,
    139   IN UINT64                NewInstructionPointer
    140   )
    141 {
    142   UINT64                      OriginalInstructionPointer;
    143   QEMU_SMRAM_SAVE_STATE_MAP  *CpuSaveState = (QEMU_SMRAM_SAVE_STATE_MAP *)CpuState;
    144 
    145   if ((CpuSaveState->x86.SMMRevId & 0xFFFF) == 0) {
    146     OriginalInstructionPointer = (UINT64)CpuSaveState->x86._EIP;
    147     CpuSaveState->x86._EIP = (UINT32)NewInstructionPointer;
    148     //
    149     // Clear the auto HALT restart flag so the RSM instruction returns
    150     // program control to the instruction following the HLT instruction.
    151     //
    152     if ((CpuSaveState->x86.AutoHALTRestart & BIT0) != 0) {
    153       CpuSaveState->x86.AutoHALTRestart &= ~BIT0;
    154     }
    155   } else {
    156     OriginalInstructionPointer = CpuSaveState->x64._RIP;
    157     if ((CpuSaveState->x64.IA32_EFER & LMA) == 0) {
    158       CpuSaveState->x64._RIP = (UINT32)NewInstructionPointer32;
    159     } else {
    160       CpuSaveState->x64._RIP = (UINT32)NewInstructionPointer;
    161     }
    162     //
    163     // Clear the auto HALT restart flag so the RSM instruction returns
    164     // program control to the instruction following the HLT instruction.
    165     //
    166     if ((CpuSaveState->x64.AutoHALTRestart & BIT0) != 0) {
    167       CpuSaveState->x64.AutoHALTRestart &= ~BIT0;
    168     }
    169   }
    170   return OriginalInstructionPointer;
    171 }
    172 
    173 /**
    174   Hook point in normal execution mode that allows the one CPU that was elected
    175   as monarch during System Management Mode initialization to perform additional
    176   initialization actions immediately after all of the CPUs have processed their
    177   first SMI and called SmmCpuFeaturesInitializeProcessor() relocating SMBASE
    178   into a buffer in SMRAM and called SmmCpuFeaturesHookReturnFromSmm().
    179 **/
    180 VOID
    181 EFIAPI
    182 SmmCpuFeaturesSmmRelocationComplete (
    183   VOID
    184   )
    185 {
    186 }
    187 
    188 /**
    189   Return the size, in bytes, of a custom SMI Handler in bytes.  If 0 is
    190   returned, then a custom SMI handler is not provided by this library,
    191   and the default SMI handler must be used.
    192 
    193   @retval 0    Use the default SMI handler.
    194   @retval > 0  Use the SMI handler installed by SmmCpuFeaturesInstallSmiHandler()
    195                The caller is required to allocate enough SMRAM for each CPU to
    196                support the size of the custom SMI handler.
    197 **/
    198 UINTN
    199 EFIAPI
    200 SmmCpuFeaturesGetSmiHandlerSize (
    201   VOID
    202   )
    203 {
    204   return 0;
    205 }
    206 
    207 /**
    208   Install a custom SMI handler for the CPU specified by CpuIndex.  This function
    209   is only called if SmmCpuFeaturesGetSmiHandlerSize() returns a size is greater
    210   than zero and is called by the CPU that was elected as monarch during System
    211   Management Mode initialization.
    212 
    213   @param[in] CpuIndex   The index of the CPU to install the custom SMI handler.
    214                         The value must be between 0 and the NumberOfCpus field
    215                         in the System Management System Table (SMST).
    216   @param[in] SmBase     The SMBASE address for the CPU specified by CpuIndex.
    217   @param[in] SmiStack   The stack to use when an SMI is processed by the
    218                         the CPU specified by CpuIndex.
    219   @param[in] StackSize  The size, in bytes, if the stack used when an SMI is
    220                         processed by the CPU specified by CpuIndex.
    221   @param[in] GdtBase    The base address of the GDT to use when an SMI is
    222                         processed by the CPU specified by CpuIndex.
    223   @param[in] GdtSize    The size, in bytes, of the GDT used when an SMI is
    224                         processed by the CPU specified by CpuIndex.
    225   @param[in] IdtBase    The base address of the IDT to use when an SMI is
    226                         processed by the CPU specified by CpuIndex.
    227   @param[in] IdtSize    The size, in bytes, of the IDT used when an SMI is
    228                         processed by the CPU specified by CpuIndex.
    229   @param[in] Cr3        The base address of the page tables to use when an SMI
    230                         is processed by the CPU specified by CpuIndex.
    231 **/
    232 VOID
    233 EFIAPI
    234 SmmCpuFeaturesInstallSmiHandler (
    235   IN UINTN   CpuIndex,
    236   IN UINT32  SmBase,
    237   IN VOID    *SmiStack,
    238   IN UINTN   StackSize,
    239   IN UINTN   GdtBase,
    240   IN UINTN   GdtSize,
    241   IN UINTN   IdtBase,
    242   IN UINTN   IdtSize,
    243   IN UINT32  Cr3
    244   )
    245 {
    246 }
    247 
    248 /**
    249   Determines if MTRR registers must be configured to set SMRAM cache-ability
    250   when executing in System Management Mode.
    251 
    252   @retval TRUE   MTRR registers must be configured to set SMRAM cache-ability.
    253   @retval FALSE  MTRR registers do not need to be configured to set SMRAM
    254                  cache-ability.
    255 **/
    256 BOOLEAN
    257 EFIAPI
    258 SmmCpuFeaturesNeedConfigureMtrrs (
    259   VOID
    260   )
    261 {
    262   return FALSE;
    263 }
    264 
    265 /**
    266   Disable SMRR register if SMRR is supported and SmmCpuFeaturesNeedConfigureMtrrs()
    267   returns TRUE.
    268 **/
    269 VOID
    270 EFIAPI
    271 SmmCpuFeaturesDisableSmrr (
    272   VOID
    273   )
    274 {
    275   //
    276   // No SMRR support, nothing to do
    277   //
    278 }
    279 
    280 /**
    281   Enable SMRR register if SMRR is supported and SmmCpuFeaturesNeedConfigureMtrrs()
    282   returns TRUE.
    283 **/
    284 VOID
    285 EFIAPI
    286 SmmCpuFeaturesReenableSmrr (
    287   VOID
    288   )
    289 {
    290   //
    291   // No SMRR support, nothing to do
    292   //
    293 }
    294 
    295 /**
    296   Processor specific hook point each time a CPU enters System Management Mode.
    297 
    298   @param[in] CpuIndex  The index of the CPU that has entered SMM.  The value
    299                        must be between 0 and the NumberOfCpus field in the
    300                        System Management System Table (SMST).
    301 **/
    302 VOID
    303 EFIAPI
    304 SmmCpuFeaturesRendezvousEntry (
    305   IN UINTN  CpuIndex
    306   )
    307 {
    308   //
    309   // No SMRR support, nothing to do
    310   //
    311 }
    312 
    313 /**
    314   Processor specific hook point each time a CPU exits System Management Mode.
    315 
    316   @param[in] CpuIndex  The index of the CPU that is exiting SMM.  The value must
    317                        be between 0 and the NumberOfCpus field in the System
    318                        Management System Table (SMST).
    319 **/
    320 VOID
    321 EFIAPI
    322 SmmCpuFeaturesRendezvousExit (
    323   IN UINTN  CpuIndex
    324   )
    325 {
    326 }
    327 
    328 /**
    329   Check to see if an SMM register is supported by a specified CPU.
    330 
    331   @param[in] CpuIndex  The index of the CPU to check for SMM register support.
    332                        The value must be between 0 and the NumberOfCpus field
    333                        in the System Management System Table (SMST).
    334   @param[in] RegName   Identifies the SMM register to check for support.
    335 
    336   @retval TRUE   The SMM register specified by RegName is supported by the CPU
    337                  specified by CpuIndex.
    338   @retval FALSE  The SMM register specified by RegName is not supported by the
    339                  CPU specified by CpuIndex.
    340 **/
    341 BOOLEAN
    342 EFIAPI
    343 SmmCpuFeaturesIsSmmRegisterSupported (
    344   IN UINTN         CpuIndex,
    345   IN SMM_REG_NAME  RegName
    346   )
    347 {
    348   ASSERT (RegName == SmmRegFeatureControl);
    349   return FALSE;
    350 }
    351 
    352 /**
    353   Returns the current value of the SMM register for the specified CPU.
    354   If the SMM register is not supported, then 0 is returned.
    355 
    356   @param[in] CpuIndex  The index of the CPU to read the SMM register.  The
    357                        value must be between 0 and the NumberOfCpus field in
    358                        the System Management System Table (SMST).
    359   @param[in] RegName   Identifies the SMM register to read.
    360 
    361   @return  The value of the SMM register specified by RegName from the CPU
    362            specified by CpuIndex.
    363 **/
    364 UINT64
    365 EFIAPI
    366 SmmCpuFeaturesGetSmmRegister (
    367   IN UINTN         CpuIndex,
    368   IN SMM_REG_NAME  RegName
    369   )
    370 {
    371   //
    372   // This is called for SmmRegSmmDelayed, SmmRegSmmBlocked, SmmRegSmmEnable.
    373   // The last of these should actually be SmmRegSmmDisable, so we can just
    374   // return FALSE.
    375   //
    376   return 0;
    377 }
    378 
    379 /**
    380   Sets the value of an SMM register on a specified CPU.
    381   If the SMM register is not supported, then no action is performed.
    382 
    383   @param[in] CpuIndex  The index of the CPU to write the SMM register.  The
    384                        value must be between 0 and the NumberOfCpus field in
    385                        the System Management System Table (SMST).
    386   @param[in] RegName   Identifies the SMM register to write.
    387                        registers are read-only.
    388   @param[in] Value     The value to write to the SMM register.
    389 **/
    390 VOID
    391 EFIAPI
    392 SmmCpuFeaturesSetSmmRegister (
    393   IN UINTN         CpuIndex,
    394   IN SMM_REG_NAME  RegName,
    395   IN UINT64        Value
    396   )
    397 {
    398   ASSERT (FALSE);
    399 }
    400 
    401 ///
    402 /// Macro used to simplify the lookup table entries of type CPU_SMM_SAVE_STATE_LOOKUP_ENTRY
    403 ///
    404 #define SMM_CPU_OFFSET(Field) OFFSET_OF (QEMU_SMRAM_SAVE_STATE_MAP, Field)
    405 
    406 ///
    407 /// Macro used to simplify the lookup table entries of type CPU_SMM_SAVE_STATE_REGISTER_RANGE
    408 ///
    409 #define SMM_REGISTER_RANGE(Start, End) { Start, End, End - Start + 1 }
    410 
    411 ///
    412 /// Structure used to describe a range of registers
    413 ///
    414 typedef struct {
    415   EFI_SMM_SAVE_STATE_REGISTER  Start;
    416   EFI_SMM_SAVE_STATE_REGISTER  End;
    417   UINTN                        Length;
    418 } CPU_SMM_SAVE_STATE_REGISTER_RANGE;
    419 
    420 ///
    421 /// Structure used to build a lookup table to retrieve the widths and offsets
    422 /// associated with each supported EFI_SMM_SAVE_STATE_REGISTER value
    423 ///
    424 
    425 #define SMM_SAVE_STATE_REGISTER_FIRST_INDEX             1
    426 
    427 typedef struct {
    428   UINT8   Width32;
    429   UINT8   Width64;
    430   UINT16  Offset32;
    431   UINT16  Offset64Lo;
    432   UINT16  Offset64Hi;
    433   BOOLEAN Writeable;
    434 } CPU_SMM_SAVE_STATE_LOOKUP_ENTRY;
    435 
    436 ///
    437 /// Table used by GetRegisterIndex() to convert an EFI_SMM_SAVE_STATE_REGISTER
    438 /// value to an index into a table of type CPU_SMM_SAVE_STATE_LOOKUP_ENTRY
    439 ///
    440 static CONST CPU_SMM_SAVE_STATE_REGISTER_RANGE mSmmCpuRegisterRanges[] = {
    441   SMM_REGISTER_RANGE (EFI_SMM_SAVE_STATE_REGISTER_GDTBASE, EFI_SMM_SAVE_STATE_REGISTER_LDTINFO),
    442   SMM_REGISTER_RANGE (EFI_SMM_SAVE_STATE_REGISTER_ES,      EFI_SMM_SAVE_STATE_REGISTER_RIP),
    443   SMM_REGISTER_RANGE (EFI_SMM_SAVE_STATE_REGISTER_RFLAGS,  EFI_SMM_SAVE_STATE_REGISTER_CR4),
    444   { (EFI_SMM_SAVE_STATE_REGISTER)0, (EFI_SMM_SAVE_STATE_REGISTER)0, 0 }
    445 };
    446 
    447 ///
    448 /// Lookup table used to retrieve the widths and offsets associated with each
    449 /// supported EFI_SMM_SAVE_STATE_REGISTER value
    450 ///
    451 static CONST CPU_SMM_SAVE_STATE_LOOKUP_ENTRY mSmmCpuWidthOffset[] = {
    452   {0, 0, 0, 0, 0, FALSE},                                                                                                     //  Reserved
    453 
    454   //
    455   // CPU Save State registers defined in PI SMM CPU Protocol.
    456   //
    457   {0, 8, 0                            , SMM_CPU_OFFSET (x64._GDTRBase) , SMM_CPU_OFFSET (x64._GDTRBase)  + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_GDTBASE  = 4
    458   {0, 8, 0                            , SMM_CPU_OFFSET (x64._IDTRBase) , SMM_CPU_OFFSET (x64._IDTRBase)  + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_IDTBASE  = 5
    459   {0, 8, 0                            , SMM_CPU_OFFSET (x64._LDTRBase) , SMM_CPU_OFFSET (x64._LDTRBase)  + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_LDTBASE  = 6
    460   {0, 0, 0                            , SMM_CPU_OFFSET (x64._GDTRLimit), SMM_CPU_OFFSET (x64._GDTRLimit) + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_GDTLIMIT = 7
    461   {0, 0, 0                            , SMM_CPU_OFFSET (x64._IDTRLimit), SMM_CPU_OFFSET (x64._IDTRLimit) + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_IDTLIMIT = 8
    462   {0, 0, 0                            , SMM_CPU_OFFSET (x64._LDTRLimit), SMM_CPU_OFFSET (x64._LDTRLimit) + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_LDTLIMIT = 9
    463   {0, 0, 0                            , 0                              , 0                               + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_LDTINFO  = 10
    464 
    465   {4, 4, SMM_CPU_OFFSET (x86._ES)     , SMM_CPU_OFFSET (x64._ES)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_ES       = 20
    466   {4, 4, SMM_CPU_OFFSET (x86._CS)     , SMM_CPU_OFFSET (x64._CS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_CS       = 21
    467   {4, 4, SMM_CPU_OFFSET (x86._SS)     , SMM_CPU_OFFSET (x64._SS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_SS       = 22
    468   {4, 4, SMM_CPU_OFFSET (x86._DS)     , SMM_CPU_OFFSET (x64._DS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_DS       = 23
    469   {4, 4, SMM_CPU_OFFSET (x86._FS)     , SMM_CPU_OFFSET (x64._FS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_FS       = 24
    470   {4, 4, SMM_CPU_OFFSET (x86._GS)     , SMM_CPU_OFFSET (x64._GS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_GS       = 25
    471   {0, 4, 0                            , SMM_CPU_OFFSET (x64._LDTR)   , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_LDTR_SEL = 26
    472   {4, 4, SMM_CPU_OFFSET (x86._TR)     , SMM_CPU_OFFSET (x64._TR)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_TR_SEL   = 27
    473   {4, 8, SMM_CPU_OFFSET (x86._DR7)    , SMM_CPU_OFFSET (x64._DR7)    , SMM_CPU_OFFSET (x64._DR7)    + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_DR7      = 28
    474   {4, 8, SMM_CPU_OFFSET (x86._DR6)    , SMM_CPU_OFFSET (x64._DR6)    , SMM_CPU_OFFSET (x64._DR6)    + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_DR6      = 29
    475   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R8)     , SMM_CPU_OFFSET (x64._R8)     + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R8       = 30
    476   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R9)     , SMM_CPU_OFFSET (x64._R9)     + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R9       = 31
    477   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R10)    , SMM_CPU_OFFSET (x64._R10)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R10      = 32
    478   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R11)    , SMM_CPU_OFFSET (x64._R11)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R11      = 33
    479   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R12)    , SMM_CPU_OFFSET (x64._R12)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R12      = 34
    480   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R13)    , SMM_CPU_OFFSET (x64._R13)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R13      = 35
    481   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R14)    , SMM_CPU_OFFSET (x64._R14)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R14      = 36
    482   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R15)    , SMM_CPU_OFFSET (x64._R15)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R15      = 37
    483   {4, 8, SMM_CPU_OFFSET (x86._EAX)    , SMM_CPU_OFFSET (x64._RAX)    , SMM_CPU_OFFSET (x64._RAX)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RAX      = 38
    484   {4, 8, SMM_CPU_OFFSET (x86._EBX)    , SMM_CPU_OFFSET (x64._RBX)    , SMM_CPU_OFFSET (x64._RBX)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RBX      = 39
    485   {4, 8, SMM_CPU_OFFSET (x86._ECX)    , SMM_CPU_OFFSET (x64._RCX)    , SMM_CPU_OFFSET (x64._RCX)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RCX      = 40
    486   {4, 8, SMM_CPU_OFFSET (x86._EDX)    , SMM_CPU_OFFSET (x64._RDX)    , SMM_CPU_OFFSET (x64._RDX)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RDX      = 41
    487   {4, 8, SMM_CPU_OFFSET (x86._ESP)    , SMM_CPU_OFFSET (x64._RSP)    , SMM_CPU_OFFSET (x64._RSP)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RSP      = 42
    488   {4, 8, SMM_CPU_OFFSET (x86._EBP)    , SMM_CPU_OFFSET (x64._RBP)    , SMM_CPU_OFFSET (x64._RBP)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RBP      = 43
    489   {4, 8, SMM_CPU_OFFSET (x86._ESI)    , SMM_CPU_OFFSET (x64._RSI)    , SMM_CPU_OFFSET (x64._RSI)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RSI      = 44
    490   {4, 8, SMM_CPU_OFFSET (x86._EDI)    , SMM_CPU_OFFSET (x64._RDI)    , SMM_CPU_OFFSET (x64._RDI)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RDI      = 45
    491   {4, 8, SMM_CPU_OFFSET (x86._EIP)    , SMM_CPU_OFFSET (x64._RIP)    , SMM_CPU_OFFSET (x64._RIP)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RIP      = 46
    492 
    493   {4, 8, SMM_CPU_OFFSET (x86._EFLAGS) , SMM_CPU_OFFSET (x64._RFLAGS) , SMM_CPU_OFFSET (x64._RFLAGS) + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RFLAGS   = 51
    494   {4, 8, SMM_CPU_OFFSET (x86._CR0)    , SMM_CPU_OFFSET (x64._CR0)    , SMM_CPU_OFFSET (x64._CR0)    + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_CR0      = 52
    495   {4, 8, SMM_CPU_OFFSET (x86._CR3)    , SMM_CPU_OFFSET (x64._CR3)    , SMM_CPU_OFFSET (x64._CR3)    + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_CR3      = 53
    496   {0, 4, 0                            , SMM_CPU_OFFSET (x64._CR4)    , SMM_CPU_OFFSET (x64._CR4)    + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_CR4      = 54
    497 };
    498 
    499 //
    500 // No support for I/O restart
    501 //
    502 
    503 /**
    504   Read information from the CPU save state.
    505 
    506   @param  Register  Specifies the CPU register to read form the save state.
    507 
    508   @retval 0   Register is not valid
    509   @retval >0  Index into mSmmCpuWidthOffset[] associated with Register
    510 
    511 **/
    512 static UINTN
    513 GetRegisterIndex (
    514   IN EFI_SMM_SAVE_STATE_REGISTER  Register
    515   )
    516 {
    517   UINTN  Index;
    518   UINTN  Offset;
    519 
    520   for (Index = 0, Offset = SMM_SAVE_STATE_REGISTER_FIRST_INDEX; mSmmCpuRegisterRanges[Index].Length != 0; Index++) {
    521     if (Register >= mSmmCpuRegisterRanges[Index].Start && Register <= mSmmCpuRegisterRanges[Index].End) {
    522       return Register - mSmmCpuRegisterRanges[Index].Start + Offset;
    523     }
    524     Offset += mSmmCpuRegisterRanges[Index].Length;
    525   }
    526   return 0;
    527 }
    528 
    529 /**
    530   Read a CPU Save State register on the target processor.
    531 
    532   This function abstracts the differences that whether the CPU Save State register is in the
    533   IA32 CPU Save State Map or X64 CPU Save State Map.
    534 
    535   This function supports reading a CPU Save State register in SMBase relocation handler.
    536 
    537   @param[in]  CpuIndex       Specifies the zero-based index of the CPU save state.
    538   @param[in]  RegisterIndex  Index into mSmmCpuWidthOffset[] look up table.
    539   @param[in]  Width          The number of bytes to read from the CPU save state.
    540   @param[out] Buffer         Upon return, this holds the CPU register value read from the save state.
    541 
    542   @retval EFI_SUCCESS           The register was read from Save State.
    543   @retval EFI_NOT_FOUND         The register is not defined for the Save State of Processor.
    544   @retval EFI_INVALID_PARAMTER  This or Buffer is NULL.
    545 
    546 **/
    547 static EFI_STATUS
    548 ReadSaveStateRegisterByIndex (
    549   IN UINTN   CpuIndex,
    550   IN UINTN   RegisterIndex,
    551   IN UINTN   Width,
    552   OUT VOID   *Buffer
    553   )
    554 {
    555   QEMU_SMRAM_SAVE_STATE_MAP  *CpuSaveState;
    556 
    557   CpuSaveState = (QEMU_SMRAM_SAVE_STATE_MAP *)gSmst->CpuSaveState[CpuIndex];
    558 
    559   if ((CpuSaveState->x86.SMMRevId & 0xFFFF) == 0) {
    560     //
    561     // If 32-bit mode width is zero, then the specified register can not be accessed
    562     //
    563     if (mSmmCpuWidthOffset[RegisterIndex].Width32 == 0) {
    564       return EFI_NOT_FOUND;
    565     }
    566 
    567     //
    568     // If Width is bigger than the 32-bit mode width, then the specified register can not be accessed
    569     //
    570     if (Width > mSmmCpuWidthOffset[RegisterIndex].Width32) {
    571       return EFI_INVALID_PARAMETER;
    572     }
    573 
    574     //
    575     // Write return buffer
    576     //
    577     ASSERT(CpuSaveState != NULL);
    578     CopyMem(Buffer, (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset32, Width);
    579   } else {
    580     //
    581     // If 64-bit mode width is zero, then the specified register can not be accessed
    582     //
    583     if (mSmmCpuWidthOffset[RegisterIndex].Width64 == 0) {
    584       return EFI_NOT_FOUND;
    585     }
    586 
    587     //
    588     // If Width is bigger than the 64-bit mode width, then the specified register can not be accessed
    589     //
    590     if (Width > mSmmCpuWidthOffset[RegisterIndex].Width64) {
    591       return EFI_INVALID_PARAMETER;
    592     }
    593 
    594     //
    595     // Write lower 32-bits of return buffer
    596     //
    597     CopyMem(Buffer, (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Lo, MIN(4, Width));
    598     if (Width >= 4) {
    599       //
    600       // Write upper 32-bits of return buffer
    601       //
    602       CopyMem((UINT8 *)Buffer + 4, (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Hi, Width - 4);
    603     }
    604   }
    605   return EFI_SUCCESS;
    606 }
    607 
    608 /**
    609   Read an SMM Save State register on the target processor.  If this function
    610   returns EFI_UNSUPPORTED, then the caller is responsible for reading the
    611   SMM Save Sate register.
    612 
    613   @param[in]  CpuIndex  The index of the CPU to read the SMM Save State.  The
    614                         value must be between 0 and the NumberOfCpus field in
    615                         the System Management System Table (SMST).
    616   @param[in]  Register  The SMM Save State register to read.
    617   @param[in]  Width     The number of bytes to read from the CPU save state.
    618   @param[out] Buffer    Upon return, this holds the CPU register value read
    619                         from the save state.
    620 
    621   @retval EFI_SUCCESS           The register was read from Save State.
    622   @retval EFI_INVALID_PARAMTER  Buffer is NULL.
    623   @retval EFI_UNSUPPORTED       This function does not support reading Register.
    624 
    625 **/
    626 EFI_STATUS
    627 EFIAPI
    628 SmmCpuFeaturesReadSaveStateRegister (
    629   IN  UINTN                        CpuIndex,
    630   IN  EFI_SMM_SAVE_STATE_REGISTER  Register,
    631   IN  UINTN                        Width,
    632   OUT VOID                         *Buffer
    633   )
    634 {
    635   UINTN                       RegisterIndex;
    636   QEMU_SMRAM_SAVE_STATE_MAP  *CpuSaveState;
    637 
    638   //
    639   // Check for special EFI_SMM_SAVE_STATE_REGISTER_LMA
    640   //
    641   if (Register == EFI_SMM_SAVE_STATE_REGISTER_LMA) {
    642     //
    643     // Only byte access is supported for this register
    644     //
    645     if (Width != 1) {
    646       return EFI_INVALID_PARAMETER;
    647     }
    648 
    649     CpuSaveState = (QEMU_SMRAM_SAVE_STATE_MAP *)gSmst->CpuSaveState[CpuIndex];
    650 
    651     //
    652     // Check CPU mode
    653     //
    654     if ((CpuSaveState->x86.SMMRevId & 0xFFFF) == 0) {
    655       *(UINT8 *)Buffer = 32;
    656     } else {
    657       *(UINT8 *)Buffer = 64;
    658     }
    659 
    660     return EFI_SUCCESS;
    661   }
    662 
    663   //
    664   // Check for special EFI_SMM_SAVE_STATE_REGISTER_IO
    665   //
    666   if (Register == EFI_SMM_SAVE_STATE_REGISTER_IO) {
    667     return EFI_NOT_FOUND;
    668   }
    669 
    670   //
    671   // Convert Register to a register lookup table index.  Let
    672   // PiSmmCpuDxeSmm implement other special registers (currently
    673   // there is only EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID).
    674   //
    675   RegisterIndex = GetRegisterIndex (Register);
    676   if (RegisterIndex == 0) {
    677     return Register < EFI_SMM_SAVE_STATE_REGISTER_IO ? EFI_NOT_FOUND : EFI_UNSUPPORTED;
    678   }
    679 
    680   return ReadSaveStateRegisterByIndex (CpuIndex, RegisterIndex, Width, Buffer);
    681 }
    682 
    683 /**
    684   Writes an SMM Save State register on the target processor.  If this function
    685   returns EFI_UNSUPPORTED, then the caller is responsible for writing the
    686   SMM Save Sate register.
    687 
    688   @param[in] CpuIndex  The index of the CPU to write the SMM Save State.  The
    689                        value must be between 0 and the NumberOfCpus field in
    690                        the System Management System Table (SMST).
    691   @param[in] Register  The SMM Save State register to write.
    692   @param[in] Width     The number of bytes to write to the CPU save state.
    693   @param[in] Buffer    Upon entry, this holds the new CPU register value.
    694 
    695   @retval EFI_SUCCESS           The register was written to Save State.
    696   @retval EFI_INVALID_PARAMTER  Buffer is NULL.
    697   @retval EFI_UNSUPPORTED       This function does not support writing Register.
    698 **/
    699 EFI_STATUS
    700 EFIAPI
    701 SmmCpuFeaturesWriteSaveStateRegister (
    702   IN UINTN                        CpuIndex,
    703   IN EFI_SMM_SAVE_STATE_REGISTER  Register,
    704   IN UINTN                        Width,
    705   IN CONST VOID                   *Buffer
    706   )
    707 {
    708   UINTN                       RegisterIndex;
    709   QEMU_SMRAM_SAVE_STATE_MAP  *CpuSaveState;
    710 
    711   //
    712   // Writes to EFI_SMM_SAVE_STATE_REGISTER_LMA are ignored
    713   //
    714   if (Register == EFI_SMM_SAVE_STATE_REGISTER_LMA) {
    715     return EFI_SUCCESS;
    716   }
    717 
    718   //
    719   // Writes to EFI_SMM_SAVE_STATE_REGISTER_IO are not supported
    720   //
    721   if (Register == EFI_SMM_SAVE_STATE_REGISTER_IO) {
    722     return EFI_NOT_FOUND;
    723   }
    724 
    725   //
    726   // Convert Register to a register lookup table index.  Let
    727   // PiSmmCpuDxeSmm implement other special registers (currently
    728   // there is only EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID).
    729   //
    730   RegisterIndex = GetRegisterIndex (Register);
    731   if (RegisterIndex == 0) {
    732     return Register < EFI_SMM_SAVE_STATE_REGISTER_IO ? EFI_NOT_FOUND : EFI_UNSUPPORTED;
    733   }
    734 
    735   CpuSaveState = (QEMU_SMRAM_SAVE_STATE_MAP *)gSmst->CpuSaveState[CpuIndex];
    736 
    737   //
    738   // Do not write non-writable SaveState, because it will cause exception.
    739   //
    740   if (!mSmmCpuWidthOffset[RegisterIndex].Writeable) {
    741     return EFI_UNSUPPORTED;
    742   }
    743 
    744   //
    745   // Check CPU mode
    746   //
    747   if ((CpuSaveState->x86.SMMRevId & 0xFFFF) == 0) {
    748     //
    749     // If 32-bit mode width is zero, then the specified register can not be accessed
    750     //
    751     if (mSmmCpuWidthOffset[RegisterIndex].Width32 == 0) {
    752       return EFI_NOT_FOUND;
    753     }
    754 
    755     //
    756     // If Width is bigger than the 32-bit mode width, then the specified register can not be accessed
    757     //
    758     if (Width > mSmmCpuWidthOffset[RegisterIndex].Width32) {
    759       return EFI_INVALID_PARAMETER;
    760     }
    761     //
    762     // Write SMM State register
    763     //
    764     ASSERT (CpuSaveState != NULL);
    765     CopyMem((UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset32, Buffer, Width);
    766   } else {
    767     //
    768     // If 64-bit mode width is zero, then the specified register can not be accessed
    769     //
    770     if (mSmmCpuWidthOffset[RegisterIndex].Width64 == 0) {
    771       return EFI_NOT_FOUND;
    772     }
    773 
    774     //
    775     // If Width is bigger than the 64-bit mode width, then the specified register can not be accessed
    776     //
    777     if (Width > mSmmCpuWidthOffset[RegisterIndex].Width64) {
    778       return EFI_INVALID_PARAMETER;
    779     }
    780 
    781     //
    782     // Write lower 32-bits of SMM State register
    783     //
    784     CopyMem((UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Lo, Buffer, MIN (4, Width));
    785     if (Width >= 4) {
    786       //
    787       // Write upper 32-bits of SMM State register
    788       //
    789       CopyMem((UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Hi, (UINT8 *)Buffer + 4, Width - 4);
    790     }
    791   }
    792   return EFI_SUCCESS;
    793 }
    794 
    795 /**
    796   This function is hook point called after the gEfiSmmReadyToLockProtocolGuid
    797   notification is completely processed.
    798 **/
    799 VOID
    800 EFIAPI
    801 SmmCpuFeaturesCompleteSmmReadyToLock (
    802   VOID
    803   )
    804 {
    805 }
    806 
    807 /**
    808   This API provides a method for a CPU to allocate a specific region for storing page tables.
    809 
    810   This API can be called more once to allocate memory for page tables.
    811 
    812   Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the
    813   allocated buffer.  The buffer returned is aligned on a 4KB boundary.  If Pages is 0, then NULL
    814   is returned.  If there is not enough memory remaining to satisfy the request, then NULL is
    815   returned.
    816 
    817   This function can also return NULL if there is no preference on where the page tables are allocated in SMRAM.
    818 
    819   @param  Pages                 The number of 4 KB pages to allocate.
    820 
    821   @return A pointer to the allocated buffer for page tables.
    822   @retval NULL      Fail to allocate a specific region for storing page tables,
    823                     Or there is no preference on where the page tables are allocated in SMRAM.
    824 
    825 **/
    826 VOID *
    827 EFIAPI
    828 SmmCpuFeaturesAllocatePageTableMemory (
    829   IN UINTN           Pages
    830   )
    831 {
    832   return NULL;
    833 }
    834 
    835