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