mirror of
				https://git.proxmox.com/git/mirror_edk2
				synced 2025-11-04 12:40:44 +00:00 
			
		
		
		
	git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10396 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1168 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1168 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @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.
 | 
						|
 | 
						|
  Copyright (c) 2009 - 2010, Intel Corporation
 | 
						|
  All rights reserved. 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 <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>
 | 
						|
 | 
						|
///
 | 
						|
/// 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;
 | 
						|
  }
 | 
						|
 | 
						|
  if (State->x86.SMMRevId < EFI_SMM_MIN_REV_ID_x64) {
 | 
						|
    SaveState->SMBASE = State->x86.SMBASE;
 | 
						|
    SaveState->SMMRevId = State->x86.SMMRevId;
 | 
						|
    SaveState->IORestart = State->x86.IORestart;
 | 
						|
    SaveState->AutoHALTRestart = State->x86.AutoHALTRestart;
 | 
						|
  } else {
 | 
						|
    SaveState->SMBASE = State->x64.SMBASE;
 | 
						|
    SaveState->SMMRevId = State->x64.SMMRevId;
 | 
						|
    SaveState->IORestart = State->x64.IORestart;
 | 
						|
    SaveState->AutoHALTRestart = State->x64.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_SMI_CPU_SAVE_STATE *SaveState;
 | 
						|
 | 
						|
  if (ToWrite != NULL) {
 | 
						|
    SaveState = &ToWrite->Ia32SaveState;
 | 
						|
  } else {
 | 
						|
    SaveState = &mFrameworkSmst->CpuSaveState[CpuIndex].Ia32SaveState;
 | 
						|
  }
 | 
						|
  
 | 
						|
  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
 | 
						|
    /// in case it may invoke AP
 | 
						|
    ///
 | 
						|
    mFrameworkSmst->CurrentlyExecutingCpu = gSmst->CurrentlyExecutingCpu;
 | 
						|
 | 
						|
    Status = gBS->StartImage (*ImageHandle, NULL, NULL);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      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.
 | 
						|
 | 
						|
  @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 == sizeof (SMMBASE_FUNCTION_DATA));
 | 
						|
 | 
						|
  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:
 | 
						|
      ASSERT (FALSE);
 | 
						|
      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;
 | 
						|
}
 | 
						|
 |