/** @file | |
SMM Base Helper SMM driver. | |
This driver is the counterpart of the SMM Base On SMM Base2 Thunk driver. It | |
provides helping services in SMM to the SMM Base On SMM Base2 Thunk driver. | |
Caution: This module requires additional review when modified. | |
This driver will have external input - communicate buffer in SMM mode. | |
This external input must be validated carefully to avoid security issue like | |
buffer overflow, integer overflow. | |
SmmHandlerEntry() will receive untrusted input and do validation. | |
Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include <PiSmm.h> | |
#include <Library/DebugLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/SmmServicesTableLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/PeCoffLib.h> | |
#include <Library/DevicePathLib.h> | |
#include <Library/CacheMaintenanceLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/SynchronizationLib.h> | |
#include <Library/CpuLib.h> | |
#include <Library/SmmMemLib.h> | |
#include <Guid/SmmBaseThunkCommunication.h> | |
#include <Protocol/SmmBaseHelperReady.h> | |
#include <Protocol/SmmCpu.h> | |
#include <Protocol/LoadedImage.h> | |
#include <Protocol/SmmCpuSaveState.h> | |
#include <Protocol/MpService.h> | |
#include <Protocol/LoadPe32Image.h> | |
#include <Protocol/SmmReadyToLock.h> | |
/** | |
Register SMM image to SMRAM profile. | |
@param[in] FilePath File path of the image. | |
@param[in] ImageBuffer Image base address. | |
@param[in] NumberOfPage Number of page. | |
@retval TRUE Register success. | |
@retval FALSE Register fail. | |
**/ | |
BOOLEAN | |
RegisterSmramProfileImage ( | |
IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
IN PHYSICAL_ADDRESS ImageBuffer, | |
IN UINTN NumberOfPage | |
); | |
/** | |
Unregister SMM image from SMRAM profile. | |
@param[in] FilePath File path of the image. | |
@param[in] ImageBuffer Image base address. | |
@param[in] NumberOfPage Number of page. | |
@retval TRUE Unregister success. | |
@retval FALSE Unregister fail. | |
**/ | |
BOOLEAN | |
UnregisterSmramProfileImage ( | |
IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
IN PHYSICAL_ADDRESS ImageBuffer, | |
IN UINTN NumberOfPage | |
); | |
/// | |
/// Structure for tracking paired information of registered Framework SMI handler | |
/// and correpsonding dispatch handle for SMI handler thunk. | |
/// | |
typedef struct { | |
LIST_ENTRY Link; | |
EFI_HANDLE DispatchHandle; | |
EFI_HANDLE SmmImageHandle; | |
EFI_SMM_CALLBACK_ENTRY_POINT CallbackAddress; | |
VOID *CommunicationBuffer; | |
UINTN *SourceSize; | |
} CALLBACK_INFO; | |
typedef struct { | |
/// | |
/// PI SMM CPU Save State register index | |
/// | |
EFI_SMM_SAVE_STATE_REGISTER Register; | |
/// | |
/// Offset in Framework SMST | |
/// | |
UINTN Offset; | |
} CPU_SAVE_STATE_CONVERSION; | |
#define CPU_SAVE_STATE_GET_OFFSET(Field) (UINTN)(&(((EFI_SMM_CPU_SAVE_STATE *) 0)->Ia32SaveState.Field)) | |
EFI_HANDLE mDispatchHandle; | |
EFI_SMM_CPU_PROTOCOL *mSmmCpu; | |
EFI_PE32_IMAGE_PROTOCOL *mLoadPe32Image; | |
EFI_GUID mEfiSmmCpuIoGuid = EFI_SMM_CPU_IO_GUID; | |
EFI_SMM_BASE_HELPER_READY_PROTOCOL *mSmmBaseHelperReady; | |
EFI_SMM_SYSTEM_TABLE *mFrameworkSmst; | |
UINTN mNumberOfProcessors; | |
BOOLEAN mLocked = FALSE; | |
BOOLEAN mPageTableHookEnabled; | |
BOOLEAN mHookInitialized; | |
UINT64 *mCpuStatePageTable; | |
SPIN_LOCK mPFLock; | |
UINT64 mPhyMask; | |
VOID *mOriginalHandler; | |
EFI_SMM_CPU_SAVE_STATE *mShadowSaveState; | |
LIST_ENTRY mCallbackInfoListHead = INITIALIZE_LIST_HEAD_VARIABLE (mCallbackInfoListHead); | |
CPU_SAVE_STATE_CONVERSION mCpuSaveStateConvTable[] = { | |
{EFI_SMM_SAVE_STATE_REGISTER_LDTBASE , CPU_SAVE_STATE_GET_OFFSET(LDTBase)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_ES , CPU_SAVE_STATE_GET_OFFSET(ES)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_CS , CPU_SAVE_STATE_GET_OFFSET(CS)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_SS , CPU_SAVE_STATE_GET_OFFSET(SS)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_DS , CPU_SAVE_STATE_GET_OFFSET(DS)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_FS , CPU_SAVE_STATE_GET_OFFSET(FS)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_GS , CPU_SAVE_STATE_GET_OFFSET(GS)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_TR_SEL , CPU_SAVE_STATE_GET_OFFSET(TR)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_DR7 , CPU_SAVE_STATE_GET_OFFSET(DR7)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_DR6 , CPU_SAVE_STATE_GET_OFFSET(DR6)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_RAX , CPU_SAVE_STATE_GET_OFFSET(EAX)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_RBX , CPU_SAVE_STATE_GET_OFFSET(EBX)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_RCX , CPU_SAVE_STATE_GET_OFFSET(ECX)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_RDX , CPU_SAVE_STATE_GET_OFFSET(EDX)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_RSP , CPU_SAVE_STATE_GET_OFFSET(ESP)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_RBP , CPU_SAVE_STATE_GET_OFFSET(EBP)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_RSI , CPU_SAVE_STATE_GET_OFFSET(ESI)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_RDI , CPU_SAVE_STATE_GET_OFFSET(EDI)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_RIP , CPU_SAVE_STATE_GET_OFFSET(EIP)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_RFLAGS , CPU_SAVE_STATE_GET_OFFSET(EFLAGS)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_CR0 , CPU_SAVE_STATE_GET_OFFSET(CR0)}, | |
{EFI_SMM_SAVE_STATE_REGISTER_CR3 , CPU_SAVE_STATE_GET_OFFSET(CR3)} | |
}; | |
/** | |
Page fault handler. | |
**/ | |
VOID | |
PageFaultHandlerHook ( | |
VOID | |
); | |
/** | |
Read CpuSaveStates from PI for Framework use. | |
The function reads PI style CpuSaveStates of CpuIndex-th CPU for Framework driver use. If | |
ToRead is specified, the CpuSaveStates will be copied to ToRead, otherwise copied to | |
mFrameworkSmst->CpuSaveState[CpuIndex]. | |
@param[in] CpuIndex The zero-based CPU index. | |
@param[in, out] ToRead If not NULL, CpuSaveStates will be copied to it. | |
**/ | |
VOID | |
ReadCpuSaveState ( | |
IN UINTN CpuIndex, | |
IN OUT EFI_SMM_CPU_SAVE_STATE *ToRead | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Index; | |
EFI_SMM_CPU_STATE *State; | |
EFI_SMI_CPU_SAVE_STATE *SaveState; | |
State = (EFI_SMM_CPU_STATE *)gSmst->CpuSaveState[CpuIndex]; | |
if (ToRead != NULL) { | |
SaveState = &ToRead->Ia32SaveState; | |
} else { | |
SaveState = &mFrameworkSmst->CpuSaveState[CpuIndex].Ia32SaveState; | |
} | |
// | |
// Note that SMBASE/SMMRevId/IORestart/AutoHALTRestart are in same location in IA32 and X64 CPU Save State Map. | |
// | |
SaveState->SMBASE = State->x86.SMBASE; | |
SaveState->SMMRevId = State->x86.SMMRevId; | |
SaveState->IORestart = State->x86.IORestart; | |
SaveState->AutoHALTRestart = State->x86.AutoHALTRestart; | |
for (Index = 0; Index < sizeof (mCpuSaveStateConvTable) / sizeof (CPU_SAVE_STATE_CONVERSION); Index++) { | |
/// | |
/// Try to use SMM CPU Protocol to access CPU save states if possible | |
/// | |
Status = mSmmCpu->ReadSaveState ( | |
mSmmCpu, | |
(UINTN)sizeof (UINT32), | |
mCpuSaveStateConvTable[Index].Register, | |
CpuIndex, | |
((UINT8 *)SaveState) + mCpuSaveStateConvTable[Index].Offset | |
); | |
ASSERT_EFI_ERROR (Status); | |
} | |
} | |
/** | |
Write CpuSaveStates from Framework into PI. | |
The function writes back CpuSaveStates of CpuIndex-th CPU from PI to Framework. If | |
ToWrite is specified, it contains the CpuSaveStates to write from, otherwise CpuSaveStates | |
to write from mFrameworkSmst->CpuSaveState[CpuIndex]. | |
@param[in] CpuIndex The zero-based CPU index. | |
@param[in] ToWrite If not NULL, CpuSaveStates to write from. | |
**/ | |
VOID | |
WriteCpuSaveState ( | |
IN UINTN CpuIndex, | |
IN EFI_SMM_CPU_SAVE_STATE *ToWrite | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Index; | |
EFI_SMM_CPU_STATE *State; | |
EFI_SMI_CPU_SAVE_STATE *SaveState; | |
State = (EFI_SMM_CPU_STATE *)gSmst->CpuSaveState[CpuIndex]; | |
if (ToWrite != NULL) { | |
SaveState = &ToWrite->Ia32SaveState; | |
} else { | |
SaveState = &mFrameworkSmst->CpuSaveState[CpuIndex].Ia32SaveState; | |
} | |
// | |
// SMMRevId is read-only. | |
// Note that SMBASE/IORestart/AutoHALTRestart are in same location in IA32 and X64 CPU Save State Map. | |
// | |
State->x86.SMBASE = SaveState->SMBASE; | |
State->x86.IORestart = SaveState->IORestart; | |
State->x86.AutoHALTRestart = SaveState->AutoHALTRestart; | |
for (Index = 0; Index < sizeof (mCpuSaveStateConvTable) / sizeof (CPU_SAVE_STATE_CONVERSION); Index++) { | |
Status = mSmmCpu->WriteSaveState ( | |
mSmmCpu, | |
(UINTN)sizeof (UINT32), | |
mCpuSaveStateConvTable[Index].Register, | |
CpuIndex, | |
((UINT8 *)SaveState) + | |
mCpuSaveStateConvTable[Index].Offset | |
); | |
} | |
} | |
/** | |
Read or write a page that contains CpuSaveStates. Read is from PI to Framework. | |
Write is from Framework to PI. | |
This function reads or writes a page that contains CpuSaveStates. The page contains Framework | |
CpuSaveStates. On read, it reads PI style CpuSaveStates and fill the page up. On write, it | |
writes back from the page content to PI CpuSaveStates struct. | |
The first Framework CpuSaveStates (for CPU 0) is from mFrameworkSmst->CpuSaveState which is | |
page aligned. Because Framework CpuSaveStates are continuous, we can know which CPUs' SaveStates | |
are in the page start from PageAddress. | |
@param[in] PageAddress The base address for a page. | |
@param[in] IsRead TRUE for Read, FALSE for Write. | |
**/ | |
VOID | |
ReadWriteCpuStatePage ( | |
IN UINT64 PageAddress, | |
IN BOOLEAN IsRead | |
) | |
{ | |
UINTN FirstSSIndex; // Index of first CpuSaveState in the page | |
UINTN LastSSIndex; // Index of last CpuSaveState in the page | |
BOOLEAN FirstSSAligned; // Whether first CpuSaveState is page-aligned | |
BOOLEAN LastSSAligned; // Whether the end of last CpuSaveState is page-aligned | |
UINTN ClippedSize; | |
UINTN CpuIndex; | |
FirstSSIndex = ((UINTN)PageAddress - (UINTN)mFrameworkSmst->CpuSaveState) / sizeof (EFI_SMM_CPU_SAVE_STATE); | |
FirstSSAligned = TRUE; | |
if (((UINTN)PageAddress - (UINTN)mFrameworkSmst->CpuSaveState) % sizeof (EFI_SMM_CPU_SAVE_STATE) != 0) { | |
FirstSSIndex++; | |
FirstSSAligned = FALSE; | |
} | |
LastSSIndex = ((UINTN)PageAddress + SIZE_4KB - (UINTN)mFrameworkSmst->CpuSaveState - 1) / sizeof (EFI_SMM_CPU_SAVE_STATE); | |
LastSSAligned = TRUE; | |
if (((UINTN)PageAddress + SIZE_4KB - (UINTN)mFrameworkSmst->CpuSaveState) % sizeof (EFI_SMM_CPU_SAVE_STATE) != 0) { | |
LastSSIndex--; | |
LastSSAligned = FALSE; | |
} | |
for (CpuIndex = FirstSSIndex; CpuIndex <= LastSSIndex && CpuIndex < mNumberOfProcessors; CpuIndex++) { | |
if (IsRead) { | |
ReadCpuSaveState (CpuIndex, NULL); | |
} else { | |
WriteCpuSaveState (CpuIndex, NULL); | |
} | |
} | |
if (!FirstSSAligned) { | |
ReadCpuSaveState (FirstSSIndex - 1, mShadowSaveState); | |
ClippedSize = (UINTN)&mFrameworkSmst->CpuSaveState[FirstSSIndex] & (SIZE_4KB - 1); | |
if (IsRead) { | |
CopyMem ((VOID*)(UINTN)PageAddress, (VOID*)((UINTN)(mShadowSaveState + 1) - ClippedSize), ClippedSize); | |
} else { | |
CopyMem ((VOID*)((UINTN)(mShadowSaveState + 1) - ClippedSize), (VOID*)(UINTN)PageAddress, ClippedSize); | |
WriteCpuSaveState (FirstSSIndex - 1, mShadowSaveState); | |
} | |
} | |
if (!LastSSAligned && LastSSIndex + 1 < mNumberOfProcessors) { | |
ReadCpuSaveState (LastSSIndex + 1, mShadowSaveState); | |
ClippedSize = SIZE_4KB - ((UINTN)&mFrameworkSmst->CpuSaveState[LastSSIndex + 1] & (SIZE_4KB - 1)); | |
if (IsRead) { | |
CopyMem (&mFrameworkSmst->CpuSaveState[LastSSIndex + 1], mShadowSaveState, ClippedSize); | |
} else { | |
CopyMem (mShadowSaveState, &mFrameworkSmst->CpuSaveState[LastSSIndex + 1], ClippedSize); | |
WriteCpuSaveState (LastSSIndex + 1, mShadowSaveState); | |
} | |
} | |
} | |
/** | |
The page fault handler that on-demand read PI CpuSaveStates for framework use. If the fault | |
is not targeted to mFrameworkSmst->CpuSaveState range, the function will return FALSE to let | |
PageFaultHandlerHook know it needs to pass the fault over to original page fault handler. | |
@retval TRUE The page fault is correctly handled. | |
@retval FALSE The page fault is not handled and is passed through to original handler. | |
**/ | |
BOOLEAN | |
PageFaultHandler ( | |
VOID | |
) | |
{ | |
BOOLEAN IsHandled; | |
UINT64 *PageTable; | |
UINT64 PFAddress; | |
UINTN NumCpuStatePages; | |
ASSERT (mPageTableHookEnabled); | |
AcquireSpinLock (&mPFLock); | |
PageTable = (UINT64*)(UINTN)(AsmReadCr3 () & mPhyMask); | |
PFAddress = AsmReadCr2 (); | |
NumCpuStatePages = EFI_SIZE_TO_PAGES (mNumberOfProcessors * sizeof (EFI_SMM_CPU_SAVE_STATE)); | |
IsHandled = FALSE; | |
if (((UINTN)mFrameworkSmst->CpuSaveState & ~(SIZE_2MB-1)) == (PFAddress & ~(SIZE_2MB-1))) { | |
if ((UINTN)mFrameworkSmst->CpuSaveState <= PFAddress && | |
PFAddress < (UINTN)mFrameworkSmst->CpuSaveState + EFI_PAGES_TO_SIZE (NumCpuStatePages) | |
) { | |
mCpuStatePageTable[BitFieldRead64 (PFAddress, 12, 20)] |= BIT0 | BIT1; // present and rw | |
CpuFlushTlb (); | |
ReadWriteCpuStatePage (PFAddress & ~(SIZE_4KB-1), TRUE); | |
IsHandled = TRUE; | |
} else { | |
ASSERT (FALSE); | |
} | |
} | |
ReleaseSpinLock (&mPFLock); | |
return IsHandled; | |
} | |
/** | |
Write back the dirty Framework CpuSaveStates to PI. | |
The function scans the page table for dirty pages in mFrameworkSmst->CpuSaveState | |
to write back to PI CpuSaveStates. It is meant to be called on each SmmBaseHelper SMI | |
callback after Framework handler is called. | |
**/ | |
VOID | |
WriteBackDirtyPages ( | |
VOID | |
) | |
{ | |
UINTN NumCpuStatePages; | |
UINTN PTIndex; | |
UINTN PTStartIndex; | |
UINTN PTEndIndex; | |
NumCpuStatePages = EFI_SIZE_TO_PAGES (mNumberOfProcessors * sizeof (EFI_SMM_CPU_SAVE_STATE)); | |
PTStartIndex = (UINTN)BitFieldRead64 ((UINT64) (UINTN) mFrameworkSmst->CpuSaveState, 12, 20); | |
PTEndIndex = (UINTN)BitFieldRead64 ((UINT64) (UINTN) mFrameworkSmst->CpuSaveState + EFI_PAGES_TO_SIZE(NumCpuStatePages) - 1, 12, 20); | |
for (PTIndex = PTStartIndex; PTIndex <= PTEndIndex; PTIndex++) { | |
if ((mCpuStatePageTable[PTIndex] & (BIT0|BIT6)) == (BIT0|BIT6)) { // present and dirty? | |
ReadWriteCpuStatePage (mCpuStatePageTable[PTIndex] & mPhyMask, FALSE); | |
} | |
} | |
} | |
/** | |
Hook IDT with our page fault handler so that the on-demand paging works on page fault. | |
The function hooks the IDT with PageFaultHandlerHook to get on-demand paging work for | |
PI<->Framework CpuSaveStates marshalling. It also saves original handler for pass-through | |
purpose. | |
**/ | |
VOID | |
HookPageFaultHandler ( | |
VOID | |
) | |
{ | |
IA32_DESCRIPTOR Idtr; | |
IA32_IDT_GATE_DESCRIPTOR *IdtGateDesc; | |
UINT32 OffsetUpper; | |
InitializeSpinLock (&mPFLock); | |
AsmReadIdtr (&Idtr); | |
IdtGateDesc = (IA32_IDT_GATE_DESCRIPTOR *) Idtr.Base; | |
OffsetUpper = *(UINT32*)((UINT64*)IdtGateDesc + 1); | |
mOriginalHandler = (VOID *)(UINTN)(LShiftU64 (OffsetUpper, 32) + IdtGateDesc[14].Bits.OffsetLow + (IdtGateDesc[14].Bits.OffsetHigh << 16)); | |
IdtGateDesc[14].Bits.OffsetLow = (UINT32)((UINTN)PageFaultHandlerHook & ((1 << 16) - 1)); | |
IdtGateDesc[14].Bits.OffsetHigh = (UINT32)(((UINTN)PageFaultHandlerHook >> 16) & ((1 << 16) - 1)); | |
} | |
/** | |
Initialize page table for pages contain HookData. | |
The function initialize PDE for 2MB range that contains HookData. If the related PDE points | |
to a 2MB page, a page table will be allocated and initialized for 4KB pages. Otherwise we juse | |
use the original page table. | |
@param[in] HookData Based on which to initialize page table. | |
@return The pointer to a Page Table that points to 4KB pages which contain HookData. | |
**/ | |
UINT64 * | |
InitCpuStatePageTable ( | |
IN VOID *HookData | |
) | |
{ | |
UINTN Index; | |
UINT64 *PageTable; | |
UINT64 *Pdpte; | |
UINT64 HookAddress; | |
UINT64 Pde; | |
UINT64 Address; | |
// | |
// Initialize physical address mask | |
// NOTE: Physical memory above virtual address limit is not supported !!! | |
// | |
AsmCpuid (0x80000008, (UINT32*)&Index, NULL, NULL, NULL); | |
mPhyMask = LShiftU64 (1, (UINT8)Index) - 1; | |
mPhyMask &= (1ull << 48) - EFI_PAGE_SIZE; | |
HookAddress = (UINT64)(UINTN)HookData; | |
PageTable = (UINT64 *)(UINTN)(AsmReadCr3 () & mPhyMask); | |
PageTable = (UINT64 *)(UINTN)(PageTable[BitFieldRead64 (HookAddress, 39, 47)] & mPhyMask); | |
PageTable = (UINT64 *)(UINTN)(PageTable[BitFieldRead64 (HookAddress, 30, 38)] & mPhyMask); | |
Pdpte = (UINT64 *)(UINTN)PageTable; | |
Pde = Pdpte[BitFieldRead64 (HookAddress, 21, 29)]; | |
ASSERT ((Pde & BIT0) != 0); // Present and 2M Page | |
if ((Pde & BIT7) == 0) { // 4KB Page Directory | |
PageTable = (UINT64 *)(UINTN)(Pde & mPhyMask); | |
} else { | |
ASSERT ((Pde & mPhyMask) == (HookAddress & ~(SIZE_2MB-1))); // 2MB Page Point to HookAddress | |
PageTable = AllocatePages (1); | |
ASSERT (PageTable != NULL); | |
Address = HookAddress & ~(SIZE_2MB-1); | |
for (Index = 0; Index < 512; Index++) { | |
PageTable[Index] = Address | BIT0 | BIT1; // Present and RW | |
Address += SIZE_4KB; | |
} | |
Pdpte[BitFieldRead64 (HookAddress, 21, 29)] = (UINT64)(UINTN)PageTable | BIT0 | BIT1; // Present and RW | |
} | |
return PageTable; | |
} | |
/** | |
Mark all the CpuSaveStates as not present. | |
The function marks all CpuSaveStates memory range as not present so that page fault can be triggered | |
on CpuSaveStates access. It is meant to be called on each SmmBaseHelper SMI callback before Framework | |
handler is called. | |
@param[in] CpuSaveState The base of CpuSaveStates. | |
**/ | |
VOID | |
HookCpuStateMemory ( | |
IN EFI_SMM_CPU_SAVE_STATE *CpuSaveState | |
) | |
{ | |
UINT64 Index; | |
UINT64 PTStartIndex; | |
UINT64 PTEndIndex; | |
PTStartIndex = BitFieldRead64 ((UINTN)CpuSaveState, 12, 20); | |
PTEndIndex = BitFieldRead64 ((UINTN)CpuSaveState + mNumberOfProcessors * sizeof (EFI_SMM_CPU_SAVE_STATE) - 1, 12, 20); | |
for (Index = PTStartIndex; Index <= PTEndIndex; Index++) { | |
mCpuStatePageTable[Index] &= ~(BIT0|BIT5|BIT6); // not present nor accessed nor dirty | |
} | |
} | |
/** | |
Framework SMST SmmInstallConfigurationTable() Thunk. | |
This thunk calls the PI SMM SmmInstallConfigurationTable() and then update the configuration | |
table related fields in the Framework SMST because the PI SMM SmmInstallConfigurationTable() | |
function may modify these fields. | |
@param[in] SystemTable A pointer to the SMM System Table. | |
@param[in] Guid A pointer to the GUID for the entry to add, update, or remove. | |
@param[in] Table A pointer to the buffer of the table to add. | |
@param[in] TableSize The size of the table to install. | |
@retval EFI_SUCCESS The (Guid, Table) pair was added, updated, or removed. | |
@retval EFI_INVALID_PARAMETER Guid is not valid. | |
@retval EFI_NOT_FOUND An attempt was made to delete a non-existent entry. | |
@retval EFI_OUT_OF_RESOURCES There is not enough memory available to complete the operation. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmmInstallConfigurationTable ( | |
IN EFI_SMM_SYSTEM_TABLE *SystemTable, | |
IN EFI_GUID *Guid, | |
IN VOID *Table, | |
IN UINTN TableSize | |
) | |
{ | |
EFI_STATUS Status; | |
Status = gSmst->SmmInstallConfigurationTable (gSmst, Guid, Table, TableSize); | |
if (!EFI_ERROR (Status)) { | |
mFrameworkSmst->NumberOfTableEntries = gSmst->NumberOfTableEntries; | |
mFrameworkSmst->SmmConfigurationTable = gSmst->SmmConfigurationTable; | |
} | |
return Status; | |
} | |
/** | |
Initialize all the stuff needed for on-demand paging hooks for PI<->Framework | |
CpuSaveStates marshalling. | |
@param[in] FrameworkSmst Framework SMM system table pointer. | |
**/ | |
VOID | |
InitHook ( | |
IN EFI_SMM_SYSTEM_TABLE *FrameworkSmst | |
) | |
{ | |
UINTN NumCpuStatePages; | |
UINTN CpuStatePage; | |
UINTN Bottom2MPage; | |
UINTN Top2MPage; | |
mPageTableHookEnabled = FALSE; | |
NumCpuStatePages = EFI_SIZE_TO_PAGES (mNumberOfProcessors * sizeof (EFI_SMM_CPU_SAVE_STATE)); | |
// | |
// Only hook page table for X64 image and less than 2MB needed to hold all CPU Save States | |
// | |
if (EFI_IMAGE_MACHINE_TYPE_SUPPORTED(EFI_IMAGE_MACHINE_X64) && NumCpuStatePages <= EFI_SIZE_TO_PAGES (SIZE_2MB)) { | |
// | |
// Allocate double page size to make sure all CPU Save States are in one 2MB page. | |
// | |
CpuStatePage = (UINTN)AllocatePages (NumCpuStatePages * 2); | |
ASSERT (CpuStatePage != 0); | |
Bottom2MPage = CpuStatePage & ~(SIZE_2MB-1); | |
Top2MPage = (CpuStatePage + EFI_PAGES_TO_SIZE (NumCpuStatePages * 2) - 1) & ~(SIZE_2MB-1); | |
if (Bottom2MPage == Top2MPage || | |
CpuStatePage + EFI_PAGES_TO_SIZE (NumCpuStatePages * 2) - Top2MPage >= EFI_PAGES_TO_SIZE (NumCpuStatePages) | |
) { | |
// | |
// If the allocated 4KB pages are within the same 2MB page or higher portion is larger, use higher portion pages. | |
// | |
FrameworkSmst->CpuSaveState = (EFI_SMM_CPU_SAVE_STATE *)(CpuStatePage + EFI_PAGES_TO_SIZE (NumCpuStatePages)); | |
FreePages ((VOID*)CpuStatePage, NumCpuStatePages); | |
} else { | |
FrameworkSmst->CpuSaveState = (EFI_SMM_CPU_SAVE_STATE *)CpuStatePage; | |
FreePages ((VOID*)(CpuStatePage + EFI_PAGES_TO_SIZE (NumCpuStatePages)), NumCpuStatePages); | |
} | |
// | |
// Add temporary working buffer for hooking | |
// | |
mShadowSaveState = (EFI_SMM_CPU_SAVE_STATE*) AllocatePool (sizeof (EFI_SMM_CPU_SAVE_STATE)); | |
ASSERT (mShadowSaveState != NULL); | |
// | |
// Allocate and initialize 4KB Page Table for hooking CpuSaveState. | |
// Replace the original 2MB PDE with new 4KB page table. | |
// | |
mCpuStatePageTable = InitCpuStatePageTable (FrameworkSmst->CpuSaveState); | |
// | |
// Mark PTE for CpuSaveState as non-exist. | |
// | |
HookCpuStateMemory (FrameworkSmst->CpuSaveState); | |
HookPageFaultHandler (); | |
CpuFlushTlb (); | |
mPageTableHookEnabled = TRUE; | |
} | |
mHookInitialized = TRUE; | |
} | |
/** | |
Construct a Framework SMST based on the PI SMM SMST. | |
@return Pointer to the constructed Framework SMST. | |
**/ | |
EFI_SMM_SYSTEM_TABLE * | |
ConstructFrameworkSmst ( | |
VOID | |
) | |
{ | |
EFI_SMM_SYSTEM_TABLE *FrameworkSmst; | |
FrameworkSmst = (EFI_SMM_SYSTEM_TABLE *)AllocatePool (sizeof (EFI_SMM_SYSTEM_TABLE)); | |
ASSERT (FrameworkSmst != NULL); | |
/// | |
/// Copy same things from PI SMST to Framework SMST | |
/// | |
CopyMem (FrameworkSmst, gSmst, (UINTN)(&((EFI_SMM_SYSTEM_TABLE *)0)->SmmIo)); | |
CopyMem ( | |
&FrameworkSmst->SmmIo, | |
&gSmst->SmmIo, | |
sizeof (EFI_SMM_SYSTEM_TABLE) - (UINTN)(&((EFI_SMM_SYSTEM_TABLE *)0)->SmmIo) | |
); | |
/// | |
/// Update Framework SMST | |
/// | |
FrameworkSmst->Hdr.Revision = EFI_SMM_SYSTEM_TABLE_REVISION; | |
CopyGuid (&FrameworkSmst->EfiSmmCpuIoGuid, &mEfiSmmCpuIoGuid); | |
mHookInitialized = FALSE; | |
FrameworkSmst->CpuSaveState = (EFI_SMM_CPU_SAVE_STATE *)AllocateZeroPool (mNumberOfProcessors * sizeof (EFI_SMM_CPU_SAVE_STATE)); | |
ASSERT (FrameworkSmst->CpuSaveState != NULL); | |
/// | |
/// Do not support floating point state now | |
/// | |
FrameworkSmst->CpuOptionalFloatingPointState = NULL; | |
FrameworkSmst->SmmInstallConfigurationTable = SmmInstallConfigurationTable; | |
return FrameworkSmst; | |
} | |
/** | |
Load a given Framework SMM driver into SMRAM and invoke its entry point. | |
@param[in] ParentImageHandle Parent Image Handle. | |
@param[in] FilePath Location of the image to be installed as the handler. | |
@param[in] SourceBuffer Optional source buffer in case the image file | |
is in memory. | |
@param[in] SourceSize Size of the source image file, if in memory. | |
@param[out] ImageHandle The handle that the base driver uses to decode | |
the handler. Unique among SMM handlers only, | |
not unique across DXE/EFI. | |
@retval EFI_SUCCESS The operation was successful. | |
@retval EFI_OUT_OF_RESOURCES There were no additional SMRAM resources to load the handler | |
@retval EFI_UNSUPPORTED Can not find its copy in normal memory. | |
@retval EFI_INVALID_PARAMETER The handlers was not the correct image type | |
**/ | |
EFI_STATUS | |
LoadImage ( | |
IN EFI_HANDLE ParentImageHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
IN VOID *SourceBuffer, | |
IN UINTN SourceSize, | |
OUT EFI_HANDLE *ImageHandle | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN PageCount; | |
UINTN OrgPageCount; | |
EFI_PHYSICAL_ADDRESS DstBuffer; | |
if (FilePath == NULL || ImageHandle == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
PageCount = 1; | |
do { | |
OrgPageCount = PageCount; | |
DstBuffer = (UINTN)-1; | |
Status = gSmst->SmmAllocatePages ( | |
AllocateMaxAddress, | |
EfiRuntimeServicesCode, | |
PageCount, | |
&DstBuffer | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = mLoadPe32Image->LoadPeImage ( | |
mLoadPe32Image, | |
ParentImageHandle, | |
FilePath, | |
SourceBuffer, | |
SourceSize, | |
DstBuffer, | |
&PageCount, | |
ImageHandle, | |
NULL, | |
EFI_LOAD_PE_IMAGE_ATTRIBUTE_NONE | |
); | |
if (EFI_ERROR (Status)) { | |
FreePages ((VOID *)(UINTN)DstBuffer, OrgPageCount); | |
} | |
} while (Status == EFI_BUFFER_TOO_SMALL); | |
if (!EFI_ERROR (Status)) { | |
/// | |
/// Update MP state in Framework SMST before transferring control to Framework SMM driver entry point | |
/// | |
mFrameworkSmst->SmmStartupThisAp = gSmst->SmmStartupThisAp; | |
mFrameworkSmst->NumberOfCpus = mNumberOfProcessors; | |
mFrameworkSmst->CurrentlyExecutingCpu = gSmst->CurrentlyExecutingCpu; | |
RegisterSmramProfileImage (FilePath, DstBuffer, PageCount); | |
Status = gBS->StartImage (*ImageHandle, NULL, NULL); | |
if (EFI_ERROR (Status)) { | |
UnregisterSmramProfileImage (FilePath, DstBuffer, PageCount); | |
mLoadPe32Image->UnLoadPeImage (mLoadPe32Image, *ImageHandle); | |
*ImageHandle = NULL; | |
FreePages ((VOID *)(UINTN)DstBuffer, PageCount); | |
} | |
} | |
return Status; | |
} | |
/** | |
Thunk service of EFI_SMM_BASE_PROTOCOL.Register(). | |
@param[in, out] FunctionData Pointer to SMMBASE_FUNCTION_DATA. | |
**/ | |
VOID | |
Register ( | |
IN OUT SMMBASE_FUNCTION_DATA *FunctionData | |
) | |
{ | |
EFI_STATUS Status; | |
if (mLocked || FunctionData->Args.Register.LegacyIA32Binary) { | |
Status = EFI_UNSUPPORTED; | |
} else { | |
Status = LoadImage ( | |
FunctionData->SmmBaseImageHandle, | |
FunctionData->Args.Register.FilePath, | |
FunctionData->Args.Register.SourceBuffer, | |
FunctionData->Args.Register.SourceSize, | |
FunctionData->Args.Register.ImageHandle | |
); | |
} | |
FunctionData->Status = Status; | |
} | |
/** | |
Thunk service of EFI_SMM_BASE_PROTOCOL.UnRegister(). | |
@param[in, out] FunctionData Pointer to SMMBASE_FUNCTION_DATA. | |
**/ | |
VOID | |
UnRegister ( | |
IN OUT SMMBASE_FUNCTION_DATA *FunctionData | |
) | |
{ | |
/// | |
/// Unregister not supported now | |
/// | |
FunctionData->Status = EFI_UNSUPPORTED; | |
} | |
/** | |
Search for Framework SMI handler information according to specific PI SMM dispatch handle. | |
@param[in] DispatchHandle The unique handle assigned by SmiHandlerRegister(). | |
@return Pointer to CALLBACK_INFO. If NULL, no callback info record is found. | |
**/ | |
CALLBACK_INFO * | |
GetCallbackInfo ( | |
IN EFI_HANDLE DispatchHandle | |
) | |
{ | |
LIST_ENTRY *Node; | |
Node = GetFirstNode (&mCallbackInfoListHead); | |
while (!IsNull (&mCallbackInfoListHead, Node)) { | |
if (((CALLBACK_INFO *)Node)->DispatchHandle == DispatchHandle) { | |
return (CALLBACK_INFO *)Node; | |
} | |
Node = GetNextNode (&mCallbackInfoListHead, Node); | |
} | |
return NULL; | |
} | |
/** | |
Callback thunk for Framework SMI handler. | |
This thunk functions calls the Framework SMI handler and converts the return value | |
defined from Framework SMI handlers to a correpsonding return value defined by PI SMM. | |
@param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). | |
@param[in] Context Points to an optional handler context which was specified when the | |
handler was registered. | |
@param[in, out] CommBuffer A pointer to a collection of data in memory that will | |
be conveyed from a non-SMM environment into an SMM environment. | |
@param[in, out] CommBufferSize The size of the CommBuffer. | |
@retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers | |
should still be called. | |
@retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should | |
still be called. | |
@retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still | |
be called. | |
@retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
CallbackThunk ( | |
IN EFI_HANDLE DispatchHandle, | |
IN CONST VOID *Context OPTIONAL, | |
IN OUT VOID *CommBuffer OPTIONAL, | |
IN OUT UINTN *CommBufferSize OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
CALLBACK_INFO *CallbackInfo; | |
UINTN CpuIndex; | |
/// | |
/// Before transferring the control into the Framework SMI handler, update CPU Save States | |
/// and MP states in the Framework SMST. | |
/// | |
if (!mHookInitialized) { | |
InitHook (mFrameworkSmst); | |
} | |
if (mPageTableHookEnabled) { | |
HookCpuStateMemory (mFrameworkSmst->CpuSaveState); | |
CpuFlushTlb (); | |
} else { | |
for (CpuIndex = 0; CpuIndex < mNumberOfProcessors; CpuIndex++) { | |
ReadCpuSaveState (CpuIndex, NULL); | |
} | |
} | |
mFrameworkSmst->SmmStartupThisAp = gSmst->SmmStartupThisAp; | |
mFrameworkSmst->NumberOfCpus = mNumberOfProcessors; | |
mFrameworkSmst->CurrentlyExecutingCpu = gSmst->CurrentlyExecutingCpu; | |
/// | |
/// Search for Framework SMI handler information | |
/// | |
CallbackInfo = GetCallbackInfo (DispatchHandle); | |
ASSERT (CallbackInfo != NULL); | |
/// | |
/// Thunk into original Framwork SMI handler | |
/// | |
Status = (CallbackInfo->CallbackAddress) ( | |
CallbackInfo->SmmImageHandle, | |
CallbackInfo->CommunicationBuffer, | |
CallbackInfo->SourceSize | |
); | |
/// | |
/// Save CPU Save States in case any of them was modified | |
/// | |
if (mPageTableHookEnabled) { | |
WriteBackDirtyPages (); | |
} else { | |
for (CpuIndex = 0; CpuIndex < mNumberOfProcessors; CpuIndex++) { | |
WriteCpuSaveState (CpuIndex, NULL); | |
} | |
} | |
/// | |
/// Conversion of returned status code | |
/// | |
switch (Status) { | |
case EFI_HANDLER_SUCCESS: | |
Status = EFI_WARN_INTERRUPT_SOURCE_QUIESCED; | |
break; | |
case EFI_HANDLER_CRITICAL_EXIT: | |
case EFI_HANDLER_SOURCE_QUIESCED: | |
Status = EFI_SUCCESS; | |
break; | |
case EFI_HANDLER_SOURCE_PENDING: | |
Status = EFI_WARN_INTERRUPT_SOURCE_PENDING; | |
break; | |
} | |
return Status; | |
} | |
/** | |
Thunk service of EFI_SMM_BASE_PROTOCOL.RegisterCallback(). | |
@param[in, out] FunctionData Pointer to SMMBASE_FUNCTION_DATA. | |
**/ | |
VOID | |
RegisterCallback ( | |
IN OUT SMMBASE_FUNCTION_DATA *FunctionData | |
) | |
{ | |
CALLBACK_INFO *Buffer; | |
if (mLocked) { | |
FunctionData->Status = EFI_UNSUPPORTED; | |
return; | |
} | |
/// | |
/// Note that MakeLast and FloatingPointSave options are not supported in PI SMM | |
/// | |
/// | |
/// Allocate buffer for callback thunk information | |
/// | |
Buffer = (CALLBACK_INFO *)AllocateZeroPool (sizeof (CALLBACK_INFO)); | |
if (Buffer == NULL) { | |
FunctionData->Status = EFI_OUT_OF_RESOURCES; | |
return; | |
} | |
/// | |
/// Fill SmmImageHandle and CallbackAddress into the thunk | |
/// | |
Buffer->SmmImageHandle = FunctionData->Args.RegisterCallback.SmmImageHandle; | |
Buffer->CallbackAddress = FunctionData->Args.RegisterCallback.CallbackAddress; | |
/// | |
/// Register the thunk code as a root SMI handler | |
/// | |
FunctionData->Status = gSmst->SmiHandlerRegister ( | |
CallbackThunk, | |
NULL, | |
&Buffer->DispatchHandle | |
); | |
if (EFI_ERROR (FunctionData->Status)) { | |
FreePool (Buffer); | |
return; | |
} | |
/// | |
/// Save this callback info | |
/// | |
InsertTailList (&mCallbackInfoListHead, &Buffer->Link); | |
} | |
/** | |
Thunk service of EFI_SMM_BASE_PROTOCOL.SmmAllocatePool(). | |
@param[in, out] FunctionData Pointer to SMMBASE_FUNCTION_DATA. | |
**/ | |
VOID | |
HelperAllocatePool ( | |
IN OUT SMMBASE_FUNCTION_DATA *FunctionData | |
) | |
{ | |
if (mLocked) { | |
FunctionData->Status = EFI_UNSUPPORTED; | |
} else { | |
FunctionData->Status = gSmst->SmmAllocatePool ( | |
FunctionData->Args.AllocatePool.PoolType, | |
FunctionData->Args.AllocatePool.Size, | |
FunctionData->Args.AllocatePool.Buffer | |
); | |
} | |
} | |
/** | |
Thunk service of EFI_SMM_BASE_PROTOCOL.SmmFreePool(). | |
@param[in, out] FunctionData Pointer to SMMBASE_FUNCTION_DATA. | |
**/ | |
VOID | |
HelperFreePool ( | |
IN OUT SMMBASE_FUNCTION_DATA *FunctionData | |
) | |
{ | |
if (mLocked) { | |
FunctionData->Status = EFI_UNSUPPORTED; | |
} else { | |
FreePool (FunctionData->Args.FreePool.Buffer); | |
FunctionData->Status = EFI_SUCCESS; | |
} | |
} | |
/** | |
Thunk service of EFI_SMM_BASE_PROTOCOL.Communicate(). | |
@param[in, out] FunctionData Pointer to SMMBASE_FUNCTION_DATA. | |
**/ | |
VOID | |
HelperCommunicate ( | |
IN OUT SMMBASE_FUNCTION_DATA *FunctionData | |
) | |
{ | |
LIST_ENTRY *Node; | |
CALLBACK_INFO *CallbackInfo; | |
if (FunctionData->Args.Communicate.CommunicationBuffer == NULL) { | |
FunctionData->Status = EFI_INVALID_PARAMETER; | |
return; | |
} | |
Node = GetFirstNode (&mCallbackInfoListHead); | |
while (!IsNull (&mCallbackInfoListHead, Node)) { | |
CallbackInfo = (CALLBACK_INFO *)Node; | |
if (FunctionData->Args.Communicate.ImageHandle == CallbackInfo->SmmImageHandle) { | |
CallbackInfo->CommunicationBuffer = FunctionData->Args.Communicate.CommunicationBuffer; | |
CallbackInfo->SourceSize = FunctionData->Args.Communicate.SourceSize; | |
/// | |
/// The message was successfully posted. | |
/// | |
FunctionData->Status = EFI_SUCCESS; | |
return; | |
} | |
Node = GetNextNode (&mCallbackInfoListHead, Node); | |
} | |
FunctionData->Status = EFI_INVALID_PARAMETER; | |
} | |
/** | |
Communication service SMI Handler entry. | |
This SMI handler provides services for the SMM Base Thunk driver. | |
Caution: This function may receive untrusted input during runtime. | |
The communicate buffer is external input, so this function will do operations only if the communicate | |
buffer is outside of SMRAM so that returning the status code in the buffer won't overwrite anywhere in SMRAM. | |
@param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). | |
@param[in] RegisterContext Points to an optional handler context which was specified when the | |
handler was registered. | |
@param[in, out] CommBuffer A pointer to a collection of data in memory that will | |
be conveyed from a non-SMM environment into an SMM environment. | |
@param[in, out] CommBufferSize The size of the CommBuffer. | |
@retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers | |
should still be called. | |
@retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should | |
still be called. | |
@retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still | |
be called. | |
@retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmmHandlerEntry ( | |
IN EFI_HANDLE DispatchHandle, | |
IN CONST VOID *RegisterContext, | |
IN OUT VOID *CommBuffer, | |
IN OUT UINTN *CommBufferSize | |
) | |
{ | |
SMMBASE_FUNCTION_DATA *FunctionData; | |
ASSERT (CommBuffer != NULL); | |
ASSERT (CommBufferSize != NULL); | |
if (*CommBufferSize == sizeof (SMMBASE_FUNCTION_DATA) && | |
SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)CommBuffer, (UINT64)*CommBufferSize)) { | |
FunctionData = (SMMBASE_FUNCTION_DATA *)CommBuffer; | |
switch (FunctionData->Function) { | |
case SmmBaseFunctionRegister: | |
Register (FunctionData); | |
break; | |
case SmmBaseFunctionUnregister: | |
UnRegister (FunctionData); | |
break; | |
case SmmBaseFunctionRegisterCallback: | |
RegisterCallback (FunctionData); | |
break; | |
case SmmBaseFunctionAllocatePool: | |
HelperAllocatePool (FunctionData); | |
break; | |
case SmmBaseFunctionFreePool: | |
HelperFreePool (FunctionData); | |
break; | |
case SmmBaseFunctionCommunicate: | |
HelperCommunicate (FunctionData); | |
break; | |
default: | |
DEBUG ((EFI_D_WARN, "SmmBaseHelper: invalid SMM Base function.\n")); | |
FunctionData->Status = EFI_UNSUPPORTED; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Smm Ready To Lock event notification handler. | |
It sets a flag indicating that SMRAM has been locked. | |
@param[in] Protocol Points to the protocol's unique identifier. | |
@param[in] Interface Points to the interface instance. | |
@param[in] Handle The handle on which the interface was installed. | |
@retval EFI_SUCCESS Notification handler runs successfully. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmmReadyToLockEventNotify ( | |
IN CONST EFI_GUID *Protocol, | |
IN VOID *Interface, | |
IN EFI_HANDLE Handle | |
) | |
{ | |
mLocked = TRUE; | |
return EFI_SUCCESS; | |
} | |
/** | |
Entry point function of the SMM Base Helper SMM driver. | |
@param[in] ImageHandle The firmware allocated handle for the EFI image. | |
@param[in] SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The entry point is executed successfully. | |
@retval other Some error occurs when executing this entry point. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmmBaseHelperMain ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_MP_SERVICES_PROTOCOL *MpServices; | |
EFI_HANDLE Handle; | |
UINTN NumberOfEnabledProcessors; | |
VOID *Registration; | |
Handle = NULL; | |
/// | |
/// Locate SMM CPU Protocol which is used later to retrieve/update CPU Save States | |
/// | |
Status = gSmst->SmmLocateProtocol (&gEfiSmmCpuProtocolGuid, NULL, (VOID **) &mSmmCpu); | |
ASSERT_EFI_ERROR (Status); | |
/// | |
/// Locate PE32 Image Protocol which is used later to load Framework SMM driver | |
/// | |
Status = SystemTable->BootServices->LocateProtocol (&gEfiLoadPeImageProtocolGuid, NULL, (VOID **) &mLoadPe32Image); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Get MP Services Protocol | |
// | |
Status = SystemTable->BootServices->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpServices); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Use MP Services Protocol to retrieve the number of processors and number of enabled processors | |
// | |
Status = MpServices->GetNumberOfProcessors (MpServices, &mNumberOfProcessors, &NumberOfEnabledProcessors); | |
ASSERT_EFI_ERROR (Status); | |
/// | |
/// Interface structure of SMM BASE Helper Ready Protocol is allocated from UEFI pool | |
/// instead of SMM pool so that SMM Base Thunk driver can access it in Non-SMM mode. | |
/// | |
Status = gBS->AllocatePool ( | |
EfiBootServicesData, | |
sizeof (EFI_SMM_BASE_HELPER_READY_PROTOCOL), | |
(VOID **)&mSmmBaseHelperReady | |
); | |
ASSERT_EFI_ERROR (Status); | |
/// | |
/// Construct Framework SMST from PI SMST | |
/// | |
mFrameworkSmst = ConstructFrameworkSmst (); | |
mSmmBaseHelperReady->FrameworkSmst = mFrameworkSmst; | |
mSmmBaseHelperReady->ServiceEntry = SmmHandlerEntry; | |
// | |
// Register SMM Ready To Lock Protocol notification | |
// | |
Status = gSmst->SmmRegisterProtocolNotify ( | |
&gEfiSmmReadyToLockProtocolGuid, | |
SmmReadyToLockEventNotify, | |
&Registration | |
); | |
ASSERT_EFI_ERROR (Status); | |
/// | |
/// Register SMM Base Helper services for SMM Base Thunk driver | |
/// | |
Status = gSmst->SmiHandlerRegister (SmmHandlerEntry, &gEfiSmmBaseThunkCommunicationGuid, &mDispatchHandle); | |
ASSERT_EFI_ERROR (Status); | |
/// | |
/// Install EFI SMM Base Helper Protocol in the UEFI handle database | |
/// | |
Status = gBS->InstallProtocolInterface ( | |
&Handle, | |
&gEfiSmmBaseHelperReadyProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
mSmmBaseHelperReady | |
); | |
ASSERT_EFI_ERROR (Status); | |
return Status; | |
} | |