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