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
GetRegisterIndex(IN EFI_SMM_SAVE_STATE_REGISTER Register)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
ReadSaveStateRegisterByIndex(IN UINTN CpuIndex,IN UINTN RegisterIndex,IN UINTN Width,OUT VOID * Buffer)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
ReadSaveStateRegister(IN UINTN CpuIndex,IN EFI_SMM_SAVE_STATE_REGISTER Register,IN UINTN Width,OUT VOID * Buffer)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
WriteSaveStateRegister(IN UINTN CpuIndex,IN EFI_SMM_SAVE_STATE_REGISTER Register,IN UINTN Width,IN CONST VOID * Buffer)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
HookReturnFromSmm(IN UINTN CpuIndex,SMRAM_SAVE_STATE_MAP * CpuState,UINT64 NewInstructionPointer32,UINT64 NewInstructionPointer)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
GetSmiHandlerSize(VOID)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
InstallSmiHandler(IN UINTN CpuIndex,IN UINT32 SmBase,IN VOID * SmiStack,IN UINTN StackSize,IN UINTN GdtBase,IN UINTN GdtSize,IN UINTN IdtBase,IN UINTN IdtSize,IN UINT32 Cr3)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