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