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 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 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 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 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 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 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 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