Home | History | Annotate | Download | only in PiSmmCpuDxeSmm
      1 /** @file
      2 Provides services to access SMRAM Save State Map
      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 
     17 #include <Library/SmmCpuFeaturesLib.h>
     18 
     19 #include <Library/BaseLib.h>
     20 #include <Library/BaseMemoryLib.h>
     21 #include <Library/SmmServicesTableLib.h>
     22 #include <Library/DebugLib.h>
     23 #include <Register/Cpuid.h>
     24 #include <Register/SmramSaveStateMap.h>
     25 
     26 //
     27 // EFER register LMA bit
     28 //
     29 #define LMA BIT10
     30 
     31 ///
     32 /// Macro used to simplify the lookup table entries of type CPU_SMM_SAVE_STATE_LOOKUP_ENTRY
     33 ///
     34 #define SMM_CPU_OFFSET(Field) OFFSET_OF (SMRAM_SAVE_STATE_MAP, Field)
     35 
     36 ///
     37 /// Macro used to simplify the lookup table entries of type CPU_SMM_SAVE_STATE_REGISTER_RANGE
     38 ///
     39 #define SMM_REGISTER_RANGE(Start, End) { Start, End, End - Start + 1 }
     40 
     41 ///
     42 /// Structure used to describe a range of registers
     43 ///
     44 typedef struct {
     45   EFI_SMM_SAVE_STATE_REGISTER  Start;
     46   EFI_SMM_SAVE_STATE_REGISTER  End;
     47   UINTN                        Length;
     48 } CPU_SMM_SAVE_STATE_REGISTER_RANGE;
     49 
     50 ///
     51 /// Structure used to build a lookup table to retrieve the widths and offsets
     52 /// associated with each supported EFI_SMM_SAVE_STATE_REGISTER value
     53 ///
     54 
     55 #define SMM_SAVE_STATE_REGISTER_SMMREVID_INDEX        1
     56 #define SMM_SAVE_STATE_REGISTER_IOMISC_INDEX          2
     57 #define SMM_SAVE_STATE_REGISTER_IOMEMADDR_INDEX       3
     58 #define SMM_SAVE_STATE_REGISTER_MAX_INDEX             4
     59 
     60 typedef struct {
     61   UINT8   Width32;
     62   UINT8   Width64;
     63   UINT16  Offset32;
     64   UINT16  Offset64Lo;
     65   UINT16  Offset64Hi;
     66   BOOLEAN Writeable;
     67 } CPU_SMM_SAVE_STATE_LOOKUP_ENTRY;
     68 
     69 ///
     70 /// Structure used to build a lookup table for the IOMisc width information
     71 ///
     72 typedef struct {
     73   UINT8                        Width;
     74   EFI_SMM_SAVE_STATE_IO_WIDTH  IoWidth;
     75 } CPU_SMM_SAVE_STATE_IO_WIDTH;
     76 
     77 ///
     78 /// Variables from SMI Handler
     79 ///
     80 extern UINT32           gSmbase;
     81 extern volatile UINT32  gSmiStack;
     82 extern UINT32           gSmiCr3;
     83 extern volatile UINT8   gcSmiHandlerTemplate[];
     84 extern CONST UINT16     gcSmiHandlerSize;
     85 
     86 //
     87 // Variables used by SMI Handler
     88 //
     89 IA32_DESCRIPTOR  gSmiHandlerIdtr;
     90 
     91 ///
     92 /// Table used by GetRegisterIndex() to convert an EFI_SMM_SAVE_STATE_REGISTER
     93 /// value to an index into a table of type CPU_SMM_SAVE_STATE_LOOKUP_ENTRY
     94 ///
     95 CONST CPU_SMM_SAVE_STATE_REGISTER_RANGE mSmmCpuRegisterRanges[] = {
     96   SMM_REGISTER_RANGE (EFI_SMM_SAVE_STATE_REGISTER_GDTBASE, EFI_SMM_SAVE_STATE_REGISTER_LDTINFO),
     97   SMM_REGISTER_RANGE (EFI_SMM_SAVE_STATE_REGISTER_ES,      EFI_SMM_SAVE_STATE_REGISTER_RIP),
     98   SMM_REGISTER_RANGE (EFI_SMM_SAVE_STATE_REGISTER_RFLAGS,  EFI_SMM_SAVE_STATE_REGISTER_CR4),
     99   { (EFI_SMM_SAVE_STATE_REGISTER)0, (EFI_SMM_SAVE_STATE_REGISTER)0, 0 }
    100 };
    101 
    102 ///
    103 /// Lookup table used to retrieve the widths and offsets associated with each
    104 /// supported EFI_SMM_SAVE_STATE_REGISTER value
    105 ///
    106 CONST CPU_SMM_SAVE_STATE_LOOKUP_ENTRY mSmmCpuWidthOffset[] = {
    107   {0, 0, 0, 0, 0, FALSE},                                                                                                     //  Reserved
    108 
    109   //
    110   // Internally defined CPU Save State Registers. Not defined in PI SMM CPU Protocol.
    111   //
    112   {4, 4, SMM_CPU_OFFSET (x86.SMMRevId)  , SMM_CPU_OFFSET (x64.SMMRevId)  , 0                                 , FALSE}, // SMM_SAVE_STATE_REGISTER_SMMREVID_INDEX  = 1
    113   {4, 4, SMM_CPU_OFFSET (x86.IOMisc)    , SMM_CPU_OFFSET (x64.IOMisc)    , 0                                 , FALSE}, // SMM_SAVE_STATE_REGISTER_IOMISC_INDEX    = 2
    114   {4, 8, SMM_CPU_OFFSET (x86.IOMemAddr) , SMM_CPU_OFFSET (x64.IOMemAddr) , SMM_CPU_OFFSET (x64.IOMemAddr) + 4, FALSE}, // SMM_SAVE_STATE_REGISTER_IOMEMADDR_INDEX = 3
    115 
    116   //
    117   // CPU Save State registers defined in PI SMM CPU Protocol.
    118   //
    119   {0, 8, 0                            , SMM_CPU_OFFSET (x64.GdtBaseLoDword) , SMM_CPU_OFFSET (x64.GdtBaseHiDword), FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_GDTBASE  = 4
    120   {0, 8, 0                            , SMM_CPU_OFFSET (x64.IdtBaseLoDword) , SMM_CPU_OFFSET (x64.IdtBaseHiDword), FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_IDTBASE  = 5
    121   {0, 8, 0                            , SMM_CPU_OFFSET (x64.LdtBaseLoDword) , SMM_CPU_OFFSET (x64.LdtBaseHiDword), FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_LDTBASE  = 6
    122   {0, 0, 0                            , 0                                   , 0                                  , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_GDTLIMIT = 7
    123   {0, 0, 0                            , 0                                   , 0                                  , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_IDTLIMIT = 8
    124   {0, 0, 0                            , 0                                   , 0                                  , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_LDTLIMIT = 9
    125   {0, 0, 0                            , 0                                   , 0                                  , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_LDTINFO  = 10
    126 
    127   {4, 4, SMM_CPU_OFFSET (x86._ES)     , SMM_CPU_OFFSET (x64._ES)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_ES       = 20
    128   {4, 4, SMM_CPU_OFFSET (x86._CS)     , SMM_CPU_OFFSET (x64._CS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_CS       = 21
    129   {4, 4, SMM_CPU_OFFSET (x86._SS)     , SMM_CPU_OFFSET (x64._SS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_SS       = 22
    130   {4, 4, SMM_CPU_OFFSET (x86._DS)     , SMM_CPU_OFFSET (x64._DS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_DS       = 23
    131   {4, 4, SMM_CPU_OFFSET (x86._FS)     , SMM_CPU_OFFSET (x64._FS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_FS       = 24
    132   {4, 4, SMM_CPU_OFFSET (x86._GS)     , SMM_CPU_OFFSET (x64._GS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_GS       = 25
    133   {0, 4, 0                            , SMM_CPU_OFFSET (x64._LDTR)   , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_LDTR_SEL = 26
    134   {4, 4, SMM_CPU_OFFSET (x86._TR)     , SMM_CPU_OFFSET (x64._TR)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_TR_SEL   = 27
    135   {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
    136   {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
    137   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R8)     , SMM_CPU_OFFSET (x64._R8)     + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R8       = 30
    138   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R9)     , SMM_CPU_OFFSET (x64._R9)     + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R9       = 31
    139   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R10)    , SMM_CPU_OFFSET (x64._R10)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R10      = 32
    140   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R11)    , SMM_CPU_OFFSET (x64._R11)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R11      = 33
    141   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R12)    , SMM_CPU_OFFSET (x64._R12)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R12      = 34
    142   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R13)    , SMM_CPU_OFFSET (x64._R13)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R13      = 35
    143   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R14)    , SMM_CPU_OFFSET (x64._R14)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R14      = 36
    144   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R15)    , SMM_CPU_OFFSET (x64._R15)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R15      = 37
    145   {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
    146   {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
    147   {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
    148   {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
    149   {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
    150   {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
    151   {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
    152   {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
    153   {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
    154 
    155   {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
    156   {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
    157   {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
    158   {0, 4, 0                            , SMM_CPU_OFFSET (x64._CR4)    , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_CR4      = 54
    159 };
    160 
    161 ///
    162 /// Lookup table for the IOMisc width information
    163 ///
    164 CONST CPU_SMM_SAVE_STATE_IO_WIDTH mSmmCpuIoWidth[] = {
    165   { 0, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8  },  // Undefined           = 0
    166   { 1, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8  },  // SMM_IO_LENGTH_BYTE  = 1
    167   { 2, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT16 },  // SMM_IO_LENGTH_WORD  = 2
    168   { 0, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8  },  // Undefined           = 3
    169   { 4, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT32 },  // SMM_IO_LENGTH_DWORD = 4
    170   { 0, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8  },  // Undefined           = 5
    171   { 0, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8  },  // Undefined           = 6
    172   { 0, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8  }   // Undefined           = 7
    173 };
    174 
    175 ///
    176 /// Lookup table for the IOMisc type information
    177 ///
    178 CONST EFI_SMM_SAVE_STATE_IO_TYPE mSmmCpuIoType[] = {
    179   EFI_SMM_SAVE_STATE_IO_TYPE_OUTPUT,     // SMM_IO_TYPE_OUT_DX        = 0
    180   EFI_SMM_SAVE_STATE_IO_TYPE_INPUT,      // SMM_IO_TYPE_IN_DX         = 1
    181   EFI_SMM_SAVE_STATE_IO_TYPE_STRING,     // SMM_IO_TYPE_OUTS          = 2
    182   EFI_SMM_SAVE_STATE_IO_TYPE_STRING,     // SMM_IO_TYPE_INS           = 3
    183   (EFI_SMM_SAVE_STATE_IO_TYPE)0,         // Undefined                 = 4
    184   (EFI_SMM_SAVE_STATE_IO_TYPE)0,         // Undefined                 = 5
    185   EFI_SMM_SAVE_STATE_IO_TYPE_REP_PREFIX, // SMM_IO_TYPE_REP_OUTS      = 6
    186   EFI_SMM_SAVE_STATE_IO_TYPE_REP_PREFIX, // SMM_IO_TYPE_REP_INS       = 7
    187   EFI_SMM_SAVE_STATE_IO_TYPE_OUTPUT,     // SMM_IO_TYPE_OUT_IMMEDIATE = 8
    188   EFI_SMM_SAVE_STATE_IO_TYPE_INPUT,      // SMM_IO_TYPE_OUT_IMMEDIATE = 9
    189   (EFI_SMM_SAVE_STATE_IO_TYPE)0,         // Undefined                 = 10
    190   (EFI_SMM_SAVE_STATE_IO_TYPE)0,         // Undefined                 = 11
    191   (EFI_SMM_SAVE_STATE_IO_TYPE)0,         // Undefined                 = 12
    192   (EFI_SMM_SAVE_STATE_IO_TYPE)0,         // Undefined                 = 13
    193   (EFI_SMM_SAVE_STATE_IO_TYPE)0,         // Undefined                 = 14
    194   (EFI_SMM_SAVE_STATE_IO_TYPE)0          // Undefined                 = 15
    195 };
    196 
    197 ///
    198 /// The mode of the CPU at the time an SMI occurs
    199 ///
    200 UINT8  mSmmSaveStateRegisterLma;
    201 
    202 /**
    203   Read information from the CPU save state.
    204 
    205   @param  Register  Specifies the CPU register to read form the save state.
    206 
    207   @retval 0   Register is not valid
    208   @retval >0  Index into mSmmCpuWidthOffset[] associated with Register
    209 
    210 **/
    211 UINTN
    212 GetRegisterIndex (
    213   IN EFI_SMM_SAVE_STATE_REGISTER  Register
    214   )
    215 {
    216   UINTN  Index;
    217   UINTN  Offset;
    218 
    219   for (Index = 0, Offset = SMM_SAVE_STATE_REGISTER_MAX_INDEX; mSmmCpuRegisterRanges[Index].Length != 0; Index++) {
    220     if (Register >= mSmmCpuRegisterRanges[Index].Start && Register <= mSmmCpuRegisterRanges[Index].End) {
    221       return Register - mSmmCpuRegisterRanges[Index].Start + Offset;
    222     }
    223     Offset += mSmmCpuRegisterRanges[Index].Length;
    224   }
    225   return 0;
    226 }
    227 
    228 /**
    229   Read a CPU Save State register on the target processor.
    230 
    231   This function abstracts the differences that whether the CPU Save State register is in the
    232   IA32 CPU Save State Map or X64 CPU Save State Map.
    233 
    234   This function supports reading a CPU Save State register in SMBase relocation handler.
    235 
    236   @param[in]  CpuIndex       Specifies the zero-based index of the CPU save state.
    237   @param[in]  RegisterIndex  Index into mSmmCpuWidthOffset[] look up table.
    238   @param[in]  Width          The number of bytes to read from the CPU save state.
    239   @param[out] Buffer         Upon return, this holds the CPU register value read from the save state.
    240 
    241   @retval EFI_SUCCESS           The register was read from Save State.
    242   @retval EFI_NOT_FOUND         The register is not defined for the Save State of Processor.
    243   @retval EFI_INVALID_PARAMTER  This or Buffer is NULL.
    244 
    245 **/
    246 EFI_STATUS
    247 ReadSaveStateRegisterByIndex (
    248   IN UINTN   CpuIndex,
    249   IN UINTN   RegisterIndex,
    250   IN UINTN   Width,
    251   OUT VOID   *Buffer
    252   )
    253 {
    254   SMRAM_SAVE_STATE_MAP  *CpuSaveState;
    255 
    256   if (RegisterIndex == 0) {
    257     return EFI_NOT_FOUND;
    258   }
    259 
    260   CpuSaveState = gSmst->CpuSaveState[CpuIndex];
    261 
    262   if (mSmmSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) {
    263     //
    264     // If 32-bit mode width is zero, then the specified register can not be accessed
    265     //
    266     if (mSmmCpuWidthOffset[RegisterIndex].Width32 == 0) {
    267       return EFI_NOT_FOUND;
    268     }
    269 
    270     //
    271     // If Width is bigger than the 32-bit mode width, then the specified register can not be accessed
    272     //
    273     if (Width > mSmmCpuWidthOffset[RegisterIndex].Width32) {
    274       return EFI_INVALID_PARAMETER;
    275     }
    276 
    277     //
    278     // Write return buffer
    279     //
    280     ASSERT(CpuSaveState != NULL);
    281     CopyMem(Buffer, (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset32, Width);
    282   } else {
    283     //
    284     // If 64-bit mode width is zero, then the specified register can not be accessed
    285     //
    286     if (mSmmCpuWidthOffset[RegisterIndex].Width64 == 0) {
    287       return EFI_NOT_FOUND;
    288     }
    289 
    290     //
    291     // If Width is bigger than the 64-bit mode width, then the specified register can not be accessed
    292     //
    293     if (Width > mSmmCpuWidthOffset[RegisterIndex].Width64) {
    294       return EFI_INVALID_PARAMETER;
    295     }
    296 
    297     //
    298     // Write lower 32-bits of return buffer
    299     //
    300     CopyMem(Buffer, (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Lo, MIN(4, Width));
    301     if (Width >= 4) {
    302       //
    303       // Write upper 32-bits of return buffer
    304       //
    305       CopyMem((UINT8 *)Buffer + 4, (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Hi, Width - 4);
    306     }
    307   }
    308   return EFI_SUCCESS;
    309 }
    310 
    311 /**
    312   Read a CPU Save State register on the target processor.
    313 
    314   This function abstracts the differences that whether the CPU Save State register is in the
    315   IA32 CPU Save State Map or X64 CPU Save State Map.
    316 
    317   This function supports reading a CPU Save State register in SMBase relocation handler.
    318 
    319   @param[in]  CpuIndex       Specifies the zero-based index of the CPU save state.
    320   @param[in]  RegisterIndex  Index into mSmmCpuWidthOffset[] look up table.
    321   @param[in]  Width          The number of bytes to read from the CPU save state.
    322   @param[out] Buffer         Upon return, this holds the CPU register value read from the save state.
    323 
    324   @retval EFI_SUCCESS           The register was read from Save State.
    325   @retval EFI_NOT_FOUND         The register is not defined for the Save State of Processor.
    326   @retval EFI_INVALID_PARAMTER  This or Buffer is NULL.
    327 
    328 **/
    329 EFI_STATUS
    330 EFIAPI
    331 ReadSaveStateRegister (
    332   IN UINTN                        CpuIndex,
    333   IN EFI_SMM_SAVE_STATE_REGISTER  Register,
    334   IN UINTN                        Width,
    335   OUT VOID                        *Buffer
    336   )
    337 {
    338   UINT32                      SmmRevId;
    339   SMRAM_SAVE_STATE_IOMISC     IoMisc;
    340   EFI_SMM_SAVE_STATE_IO_INFO  *IoInfo;
    341   VOID                        *IoMemAddr;
    342 
    343   //
    344   // Check for special EFI_SMM_SAVE_STATE_REGISTER_LMA
    345   //
    346   if (Register == EFI_SMM_SAVE_STATE_REGISTER_LMA) {
    347     //
    348     // Only byte access is supported for this register
    349     //
    350     if (Width != 1) {
    351       return EFI_INVALID_PARAMETER;
    352     }
    353 
    354     *(UINT8 *)Buffer = mSmmSaveStateRegisterLma;
    355 
    356     return EFI_SUCCESS;
    357   }
    358 
    359   //
    360   // Check for special EFI_SMM_SAVE_STATE_REGISTER_IO
    361   //
    362   if (Register == EFI_SMM_SAVE_STATE_REGISTER_IO) {
    363     //
    364     // Get SMM Revision ID
    365     //
    366     ReadSaveStateRegisterByIndex (CpuIndex, SMM_SAVE_STATE_REGISTER_SMMREVID_INDEX, sizeof(SmmRevId), &SmmRevId);
    367 
    368     //
    369     // See if the CPU supports the IOMisc register in the save state
    370     //
    371     if (SmmRevId < SMRAM_SAVE_STATE_MIN_REV_ID_IOMISC) {
    372       return EFI_NOT_FOUND;
    373     }
    374 
    375     //
    376     // Get the IOMisc register value
    377     //
    378     ReadSaveStateRegisterByIndex (CpuIndex, SMM_SAVE_STATE_REGISTER_IOMISC_INDEX, sizeof(IoMisc.Uint32), &IoMisc.Uint32);
    379 
    380     //
    381     // Check for the SMI_FLAG in IOMisc
    382     //
    383     if (IoMisc.Bits.SmiFlag == 0) {
    384       return EFI_NOT_FOUND;
    385     }
    386 
    387     //
    388     // Compute index for the I/O Length and I/O Type lookup tables
    389     //
    390     if (mSmmCpuIoWidth[IoMisc.Bits.Length].Width == 0 || mSmmCpuIoType[IoMisc.Bits.Type] == 0) {
    391       return EFI_NOT_FOUND;
    392     }
    393 
    394     //
    395     // Zero the IoInfo structure that will be returned in Buffer
    396     //
    397     IoInfo = (EFI_SMM_SAVE_STATE_IO_INFO *)Buffer;
    398     ZeroMem (IoInfo, sizeof(EFI_SMM_SAVE_STATE_IO_INFO));
    399 
    400     //
    401     // Use lookup tables to help fill in all the fields of the IoInfo structure
    402     //
    403     IoInfo->IoPort = (UINT16)IoMisc.Bits.Port;
    404     IoInfo->IoWidth = mSmmCpuIoWidth[IoMisc.Bits.Length].IoWidth;
    405     IoInfo->IoType = mSmmCpuIoType[IoMisc.Bits.Type];
    406     if (IoInfo->IoType == EFI_SMM_SAVE_STATE_IO_TYPE_INPUT || IoInfo->IoType == EFI_SMM_SAVE_STATE_IO_TYPE_OUTPUT) {
    407       ReadSaveStateRegister (CpuIndex, EFI_SMM_SAVE_STATE_REGISTER_RAX, mSmmCpuIoWidth[IoMisc.Bits.Length].Width, &IoInfo->IoData);
    408     }
    409     else {
    410       ReadSaveStateRegisterByIndex(CpuIndex, SMM_SAVE_STATE_REGISTER_IOMEMADDR_INDEX, sizeof(IoMemAddr), &IoMemAddr);
    411       CopyMem(&IoInfo->IoData, IoMemAddr, mSmmCpuIoWidth[IoMisc.Bits.Length].Width);
    412     }
    413     return EFI_SUCCESS;
    414   }
    415 
    416   //
    417   // Convert Register to a register lookup table index
    418   //
    419   return ReadSaveStateRegisterByIndex (CpuIndex, GetRegisterIndex (Register), Width, Buffer);
    420 }
    421 
    422 /**
    423   Write value to a CPU Save State register on the target processor.
    424 
    425   This function abstracts the differences that whether the CPU Save State register is in the
    426   IA32 CPU Save State Map or X64 CPU Save State Map.
    427 
    428   This function supports writing a CPU Save State register in SMBase relocation handler.
    429 
    430   @param[in] CpuIndex       Specifies the zero-based index of the CPU save state.
    431   @param[in] RegisterIndex  Index into mSmmCpuWidthOffset[] look up table.
    432   @param[in] Width          The number of bytes to read from the CPU save state.
    433   @param[in] Buffer         Upon entry, this holds the new CPU register value.
    434 
    435   @retval EFI_SUCCESS           The register was written to Save State.
    436   @retval EFI_NOT_FOUND         The register is not defined for the Save State of Processor.
    437   @retval EFI_INVALID_PARAMTER  ProcessorIndex or Width is not correct.
    438 
    439 **/
    440 EFI_STATUS
    441 EFIAPI
    442 WriteSaveStateRegister (
    443   IN UINTN                        CpuIndex,
    444   IN EFI_SMM_SAVE_STATE_REGISTER  Register,
    445   IN UINTN                        Width,
    446   IN CONST VOID                   *Buffer
    447   )
    448 {
    449   UINTN                 RegisterIndex;
    450   SMRAM_SAVE_STATE_MAP  *CpuSaveState;
    451 
    452   //
    453   // Writes to EFI_SMM_SAVE_STATE_REGISTER_LMA are ignored
    454   //
    455   if (Register == EFI_SMM_SAVE_STATE_REGISTER_LMA) {
    456     return EFI_SUCCESS;
    457   }
    458 
    459   //
    460   // Writes to EFI_SMM_SAVE_STATE_REGISTER_IO are not supported
    461   //
    462   if (Register == EFI_SMM_SAVE_STATE_REGISTER_IO) {
    463     return EFI_NOT_FOUND;
    464   }
    465 
    466   //
    467   // Convert Register to a register lookup table index
    468   //
    469   RegisterIndex = GetRegisterIndex (Register);
    470   if (RegisterIndex == 0) {
    471     return EFI_NOT_FOUND;
    472   }
    473 
    474   CpuSaveState = gSmst->CpuSaveState[CpuIndex];
    475 
    476   //
    477   // Do not write non-writable SaveState, because it will cause exception.
    478   //
    479   if (!mSmmCpuWidthOffset[RegisterIndex].Writeable) {
    480     return EFI_UNSUPPORTED;
    481   }
    482 
    483   //
    484   // Check CPU mode
    485   //
    486   if (mSmmSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) {
    487     //
    488     // If 32-bit mode width is zero, then the specified register can not be accessed
    489     //
    490     if (mSmmCpuWidthOffset[RegisterIndex].Width32 == 0) {
    491       return EFI_NOT_FOUND;
    492     }
    493 
    494     //
    495     // If Width is bigger than the 32-bit mode width, then the specified register can not be accessed
    496     //
    497     if (Width > mSmmCpuWidthOffset[RegisterIndex].Width32) {
    498       return EFI_INVALID_PARAMETER;
    499     }
    500     //
    501     // Write SMM State register
    502     //
    503     ASSERT (CpuSaveState != NULL);
    504     CopyMem((UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset32, Buffer, Width);
    505   } else {
    506     //
    507     // If 64-bit mode width is zero, then the specified register can not be accessed
    508     //
    509     if (mSmmCpuWidthOffset[RegisterIndex].Width64 == 0) {
    510       return EFI_NOT_FOUND;
    511     }
    512 
    513     //
    514     // If Width is bigger than the 64-bit mode width, then the specified register can not be accessed
    515     //
    516     if (Width > mSmmCpuWidthOffset[RegisterIndex].Width64) {
    517       return EFI_INVALID_PARAMETER;
    518     }
    519 
    520     //
    521     // Write lower 32-bits of SMM State register
    522     //
    523     CopyMem((UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Lo, Buffer, MIN (4, Width));
    524     if (Width >= 4) {
    525       //
    526       // Write upper 32-bits of SMM State register
    527       //
    528       CopyMem((UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Hi, (UINT8 *)Buffer + 4, Width - 4);
    529     }
    530   }
    531   return EFI_SUCCESS;
    532 }
    533 
    534 /**
    535   Hook the code executed immediately after an RSM instruction on the currently
    536   executing CPU.  The mode of code executed immediately after RSM must be
    537   detected, and the appropriate hook must be selected.  Always clear the auto
    538   HALT restart flag if it is set.
    539 
    540   @param[in] CpuIndex                 The processor index for the currently
    541                                       executing CPU.
    542   @param[in] CpuState                 Pointer to SMRAM Save State Map for the
    543                                       currently executing CPU.
    544   @param[in] NewInstructionPointer32  Instruction pointer to use if resuming to
    545                                       32-bit mode from 64-bit SMM.
    546   @param[in] NewInstructionPointer    Instruction pointer to use if resuming to
    547                                       same mode as SMM.
    548 
    549   @retval The value of the original instruction pointer before it was hooked.
    550 
    551 **/
    552 UINT64
    553 EFIAPI
    554 HookReturnFromSmm (
    555   IN UINTN              CpuIndex,
    556   SMRAM_SAVE_STATE_MAP  *CpuState,
    557   UINT64                NewInstructionPointer32,
    558   UINT64                NewInstructionPointer
    559   )
    560 {
    561   UINT64  OriginalInstructionPointer;
    562 
    563   OriginalInstructionPointer = SmmCpuFeaturesHookReturnFromSmm (
    564                                  CpuIndex,
    565                                  CpuState,
    566                                  NewInstructionPointer32,
    567                                  NewInstructionPointer
    568                                  );
    569   if (OriginalInstructionPointer != 0) {
    570     return OriginalInstructionPointer;
    571   }
    572 
    573   if (mSmmSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) {
    574     OriginalInstructionPointer = (UINT64)CpuState->x86._EIP;
    575     CpuState->x86._EIP = (UINT32)NewInstructionPointer;
    576     //
    577     // Clear the auto HALT restart flag so the RSM instruction returns
    578     // program control to the instruction following the HLT instruction.
    579     //
    580     if ((CpuState->x86.AutoHALTRestart & BIT0) != 0) {
    581       CpuState->x86.AutoHALTRestart &= ~BIT0;
    582     }
    583   } else {
    584     OriginalInstructionPointer = CpuState->x64._RIP;
    585     if ((CpuState->x64.IA32_EFER & LMA) == 0) {
    586       CpuState->x64._RIP = (UINT32)NewInstructionPointer32;
    587     } else {
    588       CpuState->x64._RIP = (UINT32)NewInstructionPointer;
    589     }
    590     //
    591     // Clear the auto HALT restart flag so the RSM instruction returns
    592     // program control to the instruction following the HLT instruction.
    593     //
    594     if ((CpuState->x64.AutoHALTRestart & BIT0) != 0) {
    595       CpuState->x64.AutoHALTRestart &= ~BIT0;
    596     }
    597   }
    598   return OriginalInstructionPointer;
    599 }
    600 
    601 /**
    602   Get the size of the SMI Handler in bytes.
    603 
    604   @retval The size, in bytes, of the SMI Handler.
    605 
    606 **/
    607 UINTN
    608 EFIAPI
    609 GetSmiHandlerSize (
    610   VOID
    611   )
    612 {
    613   UINTN  Size;
    614 
    615   Size = SmmCpuFeaturesGetSmiHandlerSize ();
    616   if (Size != 0) {
    617     return Size;
    618   }
    619   return gcSmiHandlerSize;
    620 }
    621 
    622 /**
    623   Install the SMI handler for the CPU specified by CpuIndex.  This function
    624   is called by the CPU that was elected as monarch during System Management
    625   Mode initialization.
    626 
    627   @param[in] CpuIndex   The index of the CPU to install the custom SMI handler.
    628                         The value must be between 0 and the NumberOfCpus field
    629                         in the System Management System Table (SMST).
    630   @param[in] SmBase     The SMBASE address for the CPU specified by CpuIndex.
    631   @param[in] SmiStack   The stack to use when an SMI is processed by the
    632                         the CPU specified by CpuIndex.
    633   @param[in] StackSize  The size, in bytes, if the stack used when an SMI is
    634                         processed by the CPU specified by CpuIndex.
    635   @param[in] GdtBase    The base address of the GDT to use when an SMI is
    636                         processed by the CPU specified by CpuIndex.
    637   @param[in] GdtSize    The size, in bytes, of the GDT used when an SMI is
    638                         processed by the CPU specified by CpuIndex.
    639   @param[in] IdtBase    The base address of the IDT to use when an SMI is
    640                         processed by the CPU specified by CpuIndex.
    641   @param[in] IdtSize    The size, in bytes, of the IDT used when an SMI is
    642                         processed by the CPU specified by CpuIndex.
    643   @param[in] Cr3        The base address of the page tables to use when an SMI
    644                         is processed by the CPU specified by CpuIndex.
    645 **/
    646 VOID
    647 EFIAPI
    648 InstallSmiHandler (
    649   IN UINTN   CpuIndex,
    650   IN UINT32  SmBase,
    651   IN VOID    *SmiStack,
    652   IN UINTN   StackSize,
    653   IN UINTN   GdtBase,
    654   IN UINTN   GdtSize,
    655   IN UINTN   IdtBase,
    656   IN UINTN   IdtSize,
    657   IN UINT32  Cr3
    658   )
    659 {
    660   if (SmmCpuFeaturesGetSmiHandlerSize () != 0) {
    661     //
    662     // Install SMI handler provided by library
    663     //
    664     SmmCpuFeaturesInstallSmiHandler (
    665       CpuIndex,
    666       SmBase,
    667       SmiStack,
    668       StackSize,
    669       GdtBase,
    670       GdtSize,
    671       IdtBase,
    672       IdtSize,
    673       Cr3
    674       );
    675     return;
    676   }
    677 
    678   //
    679   // Initialize values in template before copy
    680   //
    681   gSmiStack             = (UINT32)((UINTN)SmiStack + StackSize - sizeof (UINTN));
    682   gSmiCr3               = Cr3;
    683   gSmbase               = SmBase;
    684   gSmiHandlerIdtr.Base  = IdtBase;
    685   gSmiHandlerIdtr.Limit = (UINT16)(IdtSize - 1);
    686 
    687   //
    688   // Set the value at the top of the CPU stack to the CPU Index
    689   //
    690   *(UINTN*)(UINTN)gSmiStack = CpuIndex;
    691 
    692   //
    693   // Copy template to CPU specific SMI handler location
    694   //
    695   CopyMem (
    696     (VOID*)(UINTN)(SmBase + SMM_HANDLER_OFFSET),
    697     (VOID*)gcSmiHandlerTemplate,
    698     gcSmiHandlerSize
    699     );
    700 }
    701