/** @file | |
Provides services to access SMRAM Save State Map | |
Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR> | |
Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <PiSmm.h> | |
#include <Library/SmmCpuFeaturesLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/SmmServicesTableLib.h> | |
#include <Library/DebugLib.h> | |
#include "PiSmmCpuDxeSmm.h" | |
typedef struct { | |
UINT64 Signature; // Offset 0x00 | |
UINT16 Reserved1; // Offset 0x08 | |
UINT16 Reserved2; // Offset 0x0A | |
UINT16 Reserved3; // Offset 0x0C | |
UINT16 SmmCs; // Offset 0x0E | |
UINT16 SmmDs; // Offset 0x10 | |
UINT16 SmmSs; // Offset 0x12 | |
UINT16 SmmOtherSegment; // Offset 0x14 | |
UINT16 Reserved4; // Offset 0x16 | |
UINT64 Reserved5; // Offset 0x18 | |
UINT64 Reserved6; // Offset 0x20 | |
UINT64 Reserved7; // Offset 0x28 | |
UINT64 SmmGdtPtr; // Offset 0x30 | |
UINT32 SmmGdtSize; // Offset 0x38 | |
UINT32 Reserved8; // Offset 0x3C | |
UINT64 Reserved9; // Offset 0x40 | |
UINT64 Reserved10; // Offset 0x48 | |
UINT16 Reserved11; // Offset 0x50 | |
UINT16 Reserved12; // Offset 0x52 | |
UINT32 Reserved13; // Offset 0x54 | |
UINT64 Reserved14; // Offset 0x58 | |
} PROCESSOR_SMM_DESCRIPTOR; | |
extern CONST PROCESSOR_SMM_DESCRIPTOR gcPsd; | |
// | |
// EFER register LMA bit | |
// | |
#define LMA BIT10 | |
/// | |
/// Variables from SMI Handler | |
/// | |
X86_ASSEMBLY_PATCH_LABEL gPatchSmbase; | |
X86_ASSEMBLY_PATCH_LABEL gPatchSmiStack; | |
X86_ASSEMBLY_PATCH_LABEL gPatchSmiCr3; | |
extern volatile UINT8 gcSmiHandlerTemplate[]; | |
extern CONST UINT16 gcSmiHandlerSize; | |
// | |
// Variables used by SMI Handler | |
// | |
IA32_DESCRIPTOR gSmiHandlerIdtr; | |
/// | |
/// The mode of the CPU at the time an SMI occurs | |
/// | |
UINT8 mSmmSaveStateRegisterLma; | |
/** | |
Hook the code executed immediately after an RSM instruction on the currently | |
executing CPU. The mode of code executed immediately after RSM must be | |
detected, and the appropriate hook must be selected. Always clear the auto | |
HALT restart flag if it is set. | |
@param[in] CpuIndex The processor index for the currently | |
executing CPU. | |
@param[in] CpuState Pointer to SMRAM Save State Map for the | |
currently executing CPU. | |
@param[in] NewInstructionPointer32 Instruction pointer to use if resuming to | |
32-bit mode from 64-bit SMM. | |
@param[in] NewInstructionPointer Instruction pointer to use if resuming to | |
same mode as SMM. | |
@retval The value of the original instruction pointer before it was hooked. | |
**/ | |
UINT64 | |
EFIAPI | |
HookReturnFromSmm ( | |
IN UINTN CpuIndex, | |
SMRAM_SAVE_STATE_MAP *CpuState, | |
UINT64 NewInstructionPointer32, | |
UINT64 NewInstructionPointer | |
) | |
{ | |
UINT64 OriginalInstructionPointer; | |
OriginalInstructionPointer = SmmCpuFeaturesHookReturnFromSmm ( | |
CpuIndex, | |
CpuState, | |
NewInstructionPointer32, | |
NewInstructionPointer | |
); | |
if (OriginalInstructionPointer != 0) { | |
return OriginalInstructionPointer; | |
} | |
if (mSmmSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) { | |
OriginalInstructionPointer = (UINT64)CpuState->x86._EIP; | |
CpuState->x86._EIP = (UINT32)NewInstructionPointer; | |
// | |
// Clear the auto HALT restart flag so the RSM instruction returns | |
// program control to the instruction following the HLT instruction. | |
// | |
if ((CpuState->x86.AutoHALTRestart & BIT0) != 0) { | |
CpuState->x86.AutoHALTRestart &= ~BIT0; | |
} | |
} else { | |
OriginalInstructionPointer = CpuState->x64._RIP; | |
if ((CpuState->x64.IA32_EFER & LMA) == 0) { | |
CpuState->x64._RIP = (UINT32)NewInstructionPointer32; | |
} else { | |
CpuState->x64._RIP = (UINT32)NewInstructionPointer; | |
} | |
// | |
// Clear the auto HALT restart flag so the RSM instruction returns | |
// program control to the instruction following the HLT instruction. | |
// | |
if ((CpuState->x64.AutoHALTRestart & BIT0) != 0) { | |
CpuState->x64.AutoHALTRestart &= ~BIT0; | |
} | |
} | |
return OriginalInstructionPointer; | |
} | |
/** | |
Get the size of the SMI Handler in bytes. | |
@retval The size, in bytes, of the SMI Handler. | |
**/ | |
UINTN | |
EFIAPI | |
GetSmiHandlerSize ( | |
VOID | |
) | |
{ | |
UINTN Size; | |
Size = SmmCpuFeaturesGetSmiHandlerSize (); | |
if (Size != 0) { | |
return Size; | |
} | |
return gcSmiHandlerSize; | |
} | |
/** | |
Install the SMI handler for the CPU specified by CpuIndex. This function | |
is called by the CPU that was elected as monarch during System Management | |
Mode initialization. | |
@param[in] CpuIndex The index of the CPU to install the custom SMI handler. | |
The value must be between 0 and the NumberOfCpus field | |
in the System Management System Table (SMST). | |
@param[in] SmBase The SMBASE address for the CPU specified by CpuIndex. | |
@param[in] SmiStack The stack to use when an SMI is processed by the | |
the CPU specified by CpuIndex. | |
@param[in] StackSize The size, in bytes, if the stack used when an SMI is | |
processed by the CPU specified by CpuIndex. | |
@param[in] GdtBase The base address of the GDT to use when an SMI is | |
processed by the CPU specified by CpuIndex. | |
@param[in] GdtSize The size, in bytes, of the GDT used when an SMI is | |
processed by the CPU specified by CpuIndex. | |
@param[in] IdtBase The base address of the IDT to use when an SMI is | |
processed by the CPU specified by CpuIndex. | |
@param[in] IdtSize The size, in bytes, of the IDT used when an SMI is | |
processed by the CPU specified by CpuIndex. | |
@param[in] Cr3 The base address of the page tables to use when an SMI | |
is processed by the CPU specified by CpuIndex. | |
**/ | |
VOID | |
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 | |
) | |
{ | |
PROCESSOR_SMM_DESCRIPTOR *Psd; | |
UINT32 CpuSmiStack; | |
// | |
// Initialize PROCESSOR_SMM_DESCRIPTOR | |
// | |
Psd = (PROCESSOR_SMM_DESCRIPTOR *)(VOID *)((UINTN)SmBase + SMM_PSD_OFFSET); | |
CopyMem (Psd, &gcPsd, sizeof (gcPsd)); | |
Psd->SmmGdtPtr = (UINT64)GdtBase; | |
Psd->SmmGdtSize = (UINT32)GdtSize; | |
if (SmmCpuFeaturesGetSmiHandlerSize () != 0) { | |
// | |
// Install SMI handler provided by library | |
// | |
SmmCpuFeaturesInstallSmiHandler ( | |
CpuIndex, | |
SmBase, | |
SmiStack, | |
StackSize, | |
GdtBase, | |
GdtSize, | |
IdtBase, | |
IdtSize, | |
Cr3 | |
); | |
return; | |
} | |
InitShadowStack (CpuIndex, (VOID *)((UINTN)SmiStack + StackSize)); | |
// | |
// Initialize values in template before copy | |
// | |
CpuSmiStack = (UINT32)((UINTN)SmiStack + StackSize - sizeof (UINTN)); | |
PatchInstructionX86 (gPatchSmiStack, CpuSmiStack, 4); | |
PatchInstructionX86 (gPatchSmiCr3, Cr3, 4); | |
PatchInstructionX86 (gPatchSmbase, SmBase, 4); | |
gSmiHandlerIdtr.Base = IdtBase; | |
gSmiHandlerIdtr.Limit = (UINT16)(IdtSize - 1); | |
// | |
// Set the value at the top of the CPU stack to the CPU Index | |
// | |
*(UINTN *)(UINTN)CpuSmiStack = CpuIndex; | |
// | |
// Copy template to CPU specific SMI handler location | |
// | |
CopyMem ( | |
(VOID *)((UINTN)SmBase + SMM_HANDLER_OFFSET), | |
(VOID *)gcSmiHandlerTemplate, | |
gcSmiHandlerSize | |
); | |
} |