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