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