/** @file | |
SMM Driver Dispatcher. | |
Step #1 - When a FV protocol is added to the system every driver in the FV | |
is added to the mDiscoveredList. The Before, and After Depex are | |
pre-processed as drivers are added to the mDiscoveredList. If an Apriori | |
file exists in the FV those drivers are addeded to the | |
mScheduledQueue. The mFvHandleList is used to make sure a | |
FV is only processed once. | |
Step #2 - Dispatch. Remove driver from the mScheduledQueue and load and | |
start it. After mScheduledQueue is drained check the | |
mDiscoveredList to see if any item has a Depex that is ready to | |
be placed on the mScheduledQueue. | |
Step #3 - Adding to the mScheduledQueue requires that you process Before | |
and After dependencies. This is done recursively as the call to add | |
to the mScheduledQueue checks for Before and recursively adds | |
all Befores. It then addes the item that was passed in and then | |
processes the After dependencies by recursively calling the routine. | |
Dispatcher Rules: | |
The rules for the dispatcher are similar to the DXE dispatcher. | |
The rules for DXE dispatcher are in chapter 10 of the DXE CIS. Figure 10-3 | |
is the state diagram for the DXE dispatcher | |
Depex - Dependency Expression. | |
Copyright (c) 2014, Hewlett-Packard Development Company, L.P. | |
Copyright (c) 2009 - 2023, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "PiSmmCore.h" | |
// | |
// SMM Dispatcher Data structures | |
// | |
#define KNOWN_HANDLE_SIGNATURE SIGNATURE_32('k','n','o','w') | |
typedef struct { | |
UINTN Signature; | |
LIST_ENTRY Link; // mFvHandleList | |
EFI_HANDLE Handle; | |
} KNOWN_HANDLE; | |
// | |
// Function Prototypes | |
// | |
/** | |
Insert InsertedDriverEntry onto the mScheduledQueue. To do this you | |
must add any driver with a before dependency on InsertedDriverEntry first. | |
You do this by recursively calling this routine. After all the Befores are | |
processed you can add InsertedDriverEntry to the mScheduledQueue. | |
Then you can add any driver with an After dependency on InsertedDriverEntry | |
by recursively calling this routine. | |
@param InsertedDriverEntry The driver to insert on the ScheduledLink Queue | |
**/ | |
VOID | |
SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter ( | |
IN EFI_SMM_DRIVER_ENTRY *InsertedDriverEntry | |
); | |
// | |
// The Driver List contains one copy of every driver that has been discovered. | |
// Items are never removed from the driver list. List of EFI_SMM_DRIVER_ENTRY | |
// | |
LIST_ENTRY mDiscoveredList = INITIALIZE_LIST_HEAD_VARIABLE (mDiscoveredList); | |
// | |
// Queue of drivers that are ready to dispatch. This queue is a subset of the | |
// mDiscoveredList.list of EFI_SMM_DRIVER_ENTRY. | |
// | |
LIST_ENTRY mScheduledQueue = INITIALIZE_LIST_HEAD_VARIABLE (mScheduledQueue); | |
// | |
// List of handles who's Fv's have been parsed and added to the mFwDriverList. | |
// | |
LIST_ENTRY mFvHandleList = INITIALIZE_LIST_HEAD_VARIABLE (mFvHandleList); | |
// | |
// Flag for the SMM Dispatcher. TRUE if dispatcher is executing. | |
// | |
BOOLEAN gDispatcherRunning = FALSE; | |
// | |
// Flag for the SMM Dispatcher. TRUE if there is one or more SMM drivers ready to be dispatched | |
// | |
BOOLEAN gRequestDispatch = FALSE; | |
// | |
// List of file types supported by dispatcher | |
// | |
EFI_FV_FILETYPE mSmmFileTypes[] = { | |
EFI_FV_FILETYPE_SMM, | |
EFI_FV_FILETYPE_COMBINED_SMM_DXE, | |
EFI_FV_FILETYPE_SMM_CORE, | |
// | |
// Note: DXE core will process the FV image file, so skip it in SMM core | |
// EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE | |
// | |
}; | |
typedef struct { | |
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH File; | |
EFI_DEVICE_PATH_PROTOCOL End; | |
} FV_FILEPATH_DEVICE_PATH; | |
FV_FILEPATH_DEVICE_PATH mFvDevicePath; | |
// | |
// DXE Architecture Protocols | |
// | |
EFI_SECURITY_ARCH_PROTOCOL *mSecurity = NULL; | |
EFI_SECURITY2_ARCH_PROTOCOL *mSecurity2 = NULL; | |
// | |
// The global variable is defined for Loading modules at fixed address feature to track the SMM code | |
// memory range usage. It is a bit mapped array in which every bit indicates the corresponding | |
// memory page available or not. | |
// | |
GLOBAL_REMOVE_IF_UNREFERENCED UINT64 *mSmmCodeMemoryRangeUsageBitMap = NULL; | |
/** | |
To check memory usage bit map array to figure out if the memory range in which the image will be loaded is available or not. If | |
memory range is available, the function will mark the corresponding bits to 1 which indicates the memory range is used. | |
The function is only invoked when load modules at fixed address feature is enabled. | |
@param ImageBase The base address the image will be loaded at. | |
@param ImageSize The size of the image | |
@retval EFI_SUCCESS The memory range the image will be loaded in is available | |
@retval EFI_NOT_FOUND The memory range the image will be loaded in is not available | |
**/ | |
EFI_STATUS | |
CheckAndMarkFixLoadingMemoryUsageBitMap ( | |
IN EFI_PHYSICAL_ADDRESS ImageBase, | |
IN UINTN ImageSize | |
) | |
{ | |
UINT32 SmmCodePageNumber; | |
UINT64 SmmCodeSize; | |
EFI_PHYSICAL_ADDRESS SmmCodeBase; | |
UINTN BaseOffsetPageNumber; | |
UINTN TopOffsetPageNumber; | |
UINTN Index; | |
// | |
// Build tool will calculate the smm code size and then patch the PcdLoadFixAddressSmmCodePageNumber | |
// | |
SmmCodePageNumber = PcdGet32 (PcdLoadFixAddressSmmCodePageNumber); | |
SmmCodeSize = EFI_PAGES_TO_SIZE (SmmCodePageNumber); | |
SmmCodeBase = gLoadModuleAtFixAddressSmramBase; | |
// | |
// If the memory usage bit map is not initialized, do it. Every bit in the array | |
// indicate the status of the corresponding memory page, available or not | |
// | |
if (mSmmCodeMemoryRangeUsageBitMap == NULL) { | |
mSmmCodeMemoryRangeUsageBitMap = AllocateZeroPool (((SmmCodePageNumber / 64) + 1)*sizeof (UINT64)); | |
} | |
// | |
// If the Dxe code memory range is not allocated or the bit map array allocation failed, return EFI_NOT_FOUND | |
// | |
if (mSmmCodeMemoryRangeUsageBitMap == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
// | |
// see if the memory range for loading the image is in the SMM code range. | |
// | |
if ((SmmCodeBase + SmmCodeSize < ImageBase + ImageSize) || (SmmCodeBase > ImageBase)) { | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Test if the memory is available or not. | |
// | |
BaseOffsetPageNumber = EFI_SIZE_TO_PAGES ((UINT32)(ImageBase - SmmCodeBase)); | |
TopOffsetPageNumber = EFI_SIZE_TO_PAGES ((UINT32)(ImageBase + ImageSize - SmmCodeBase)); | |
for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index++) { | |
if ((mSmmCodeMemoryRangeUsageBitMap[Index / 64] & LShiftU64 (1, (Index % 64))) != 0) { | |
// | |
// This page is already used. | |
// | |
return EFI_NOT_FOUND; | |
} | |
} | |
// | |
// Being here means the memory range is available. So mark the bits for the memory range | |
// | |
for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index++) { | |
mSmmCodeMemoryRangeUsageBitMap[Index / 64] |= LShiftU64 (1, (Index % 64)); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Get the fixed loading address from image header assigned by build tool. This function only be called | |
when Loading module at Fixed address feature enabled. | |
@param ImageContext Pointer to the image context structure that describes the PE/COFF | |
image that needs to be examined by this function. | |
@retval EFI_SUCCESS An fixed loading address is assigned to this image by build tools . | |
@retval EFI_NOT_FOUND The image has no assigned fixed loading address. | |
**/ | |
EFI_STATUS | |
GetPeCoffImageFixLoadingAssignedAddress ( | |
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext | |
) | |
{ | |
UINTN SectionHeaderOffset; | |
EFI_STATUS Status; | |
EFI_IMAGE_SECTION_HEADER SectionHeader; | |
EFI_IMAGE_OPTIONAL_HEADER_UNION *ImgHdr; | |
EFI_PHYSICAL_ADDRESS FixLoadingAddress; | |
UINT16 Index; | |
UINTN Size; | |
UINT16 NumberOfSections; | |
UINT64 ValueInSectionHeader; | |
FixLoadingAddress = 0; | |
Status = EFI_NOT_FOUND; | |
// | |
// Get PeHeader pointer | |
// | |
ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((CHAR8 *)ImageContext->Handle + ImageContext->PeCoffHeaderOffset); | |
SectionHeaderOffset = ImageContext->PeCoffHeaderOffset + | |
sizeof (UINT32) + | |
sizeof (EFI_IMAGE_FILE_HEADER) + | |
ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader; | |
NumberOfSections = ImgHdr->Pe32.FileHeader.NumberOfSections; | |
// | |
// Get base address from the first section header that doesn't point to code section. | |
// | |
for (Index = 0; Index < NumberOfSections; Index++) { | |
// | |
// Read section header from file | |
// | |
Size = sizeof (EFI_IMAGE_SECTION_HEADER); | |
Status = ImageContext->ImageRead ( | |
ImageContext->Handle, | |
SectionHeaderOffset, | |
&Size, | |
&SectionHeader | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = EFI_NOT_FOUND; | |
if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_CNT_CODE) == 0) { | |
// | |
// Build tool will save the address in PointerToRelocations & PointerToLineNumbers fields in the first section header | |
// that doesn't point to code section in image header.So there is an assumption that when the feature is enabled, | |
// if a module with a loading address assigned by tools, the PointerToRelocations & PointerToLineNumbers fields | |
// should not be Zero, or else, these 2 fields should be set to Zero | |
// | |
ValueInSectionHeader = ReadUnaligned64 ((UINT64 *)&SectionHeader.PointerToRelocations); | |
if (ValueInSectionHeader != 0) { | |
// | |
// Found first section header that doesn't point to code section in which build tool saves the | |
// offset to SMRAM base as image base in PointerToRelocations & PointerToLineNumbers fields | |
// | |
FixLoadingAddress = (EFI_PHYSICAL_ADDRESS)(gLoadModuleAtFixAddressSmramBase + (INT64)ValueInSectionHeader); | |
// | |
// Check if the memory range is available. | |
// | |
Status = CheckAndMarkFixLoadingMemoryUsageBitMap (FixLoadingAddress, (UINTN)(ImageContext->ImageSize + ImageContext->SectionAlignment)); | |
if (!EFI_ERROR (Status)) { | |
// | |
// The assigned address is valid. Return the specified loading address | |
// | |
ImageContext->ImageAddress = FixLoadingAddress; | |
} | |
} | |
break; | |
} | |
SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER); | |
} | |
DEBUG ((DEBUG_INFO|DEBUG_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address %x, Status = %r\n", FixLoadingAddress, Status)); | |
return Status; | |
} | |
/** | |
Loads an EFI image into SMRAM. | |
@param DriverEntry EFI_SMM_DRIVER_ENTRY instance | |
@return EFI_STATUS | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmmLoadImage ( | |
IN OUT EFI_SMM_DRIVER_ENTRY *DriverEntry | |
) | |
{ | |
UINT32 AuthenticationStatus; | |
UINTN FilePathSize; | |
VOID *Buffer; | |
UINTN Size; | |
UINTN PageCount; | |
EFI_GUID *NameGuid; | |
EFI_STATUS Status; | |
EFI_STATUS SecurityStatus; | |
EFI_HANDLE DeviceHandle; | |
EFI_PHYSICAL_ADDRESS DstBuffer; | |
EFI_DEVICE_PATH_PROTOCOL *FilePath; | |
EFI_DEVICE_PATH_PROTOCOL *OriginalFilePath; | |
EFI_DEVICE_PATH_PROTOCOL *HandleFilePath; | |
EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; | |
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; | |
PERF_LOAD_IMAGE_BEGIN (DriverEntry->ImageHandle); | |
Buffer = NULL; | |
Size = 0; | |
Fv = DriverEntry->Fv; | |
NameGuid = &DriverEntry->FileName; | |
FilePath = DriverEntry->FvFileDevicePath; | |
OriginalFilePath = FilePath; | |
HandleFilePath = FilePath; | |
DeviceHandle = NULL; | |
SecurityStatus = EFI_SUCCESS; | |
Status = EFI_SUCCESS; | |
AuthenticationStatus = 0; | |
// | |
// Try to get the image device handle by checking the match protocol. | |
// | |
Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &HandleFilePath, &DeviceHandle); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// If the Security2 and Security Architectural Protocol has not been located yet, then attempt to locate it | |
// | |
if (mSecurity2 == NULL) { | |
gBS->LocateProtocol (&gEfiSecurity2ArchProtocolGuid, NULL, (VOID **)&mSecurity2); | |
} | |
if (mSecurity == NULL) { | |
gBS->LocateProtocol (&gEfiSecurityArchProtocolGuid, NULL, (VOID **)&mSecurity); | |
} | |
// | |
// When Security2 is installed, Security Architectural Protocol must be published. | |
// | |
ASSERT (mSecurity2 == NULL || mSecurity != NULL); | |
// | |
// Pull out just the file portion of the DevicePath for the LoadedImage FilePath | |
// | |
FilePath = OriginalFilePath; | |
Status = gBS->HandleProtocol (DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID **)&HandleFilePath); | |
if (!EFI_ERROR (Status)) { | |
FilePathSize = GetDevicePathSize (HandleFilePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL); | |
FilePath = (EFI_DEVICE_PATH_PROTOCOL *)(((UINT8 *)FilePath) + FilePathSize); | |
} | |
// | |
// Try reading PE32 section firstly | |
// | |
Status = Fv->ReadSection ( | |
Fv, | |
NameGuid, | |
EFI_SECTION_PE32, | |
0, | |
&Buffer, | |
&Size, | |
&AuthenticationStatus | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// Try reading TE section secondly | |
// | |
Buffer = NULL; | |
Size = 0; | |
Status = Fv->ReadSection ( | |
Fv, | |
NameGuid, | |
EFI_SECTION_TE, | |
0, | |
&Buffer, | |
&Size, | |
&AuthenticationStatus | |
); | |
} | |
if (EFI_ERROR (Status)) { | |
if (Buffer != NULL) { | |
gBS->FreePool (Buffer); | |
} | |
return Status; | |
} | |
// | |
// Verify File Authentication through the Security2 Architectural Protocol | |
// | |
if (mSecurity2 != NULL) { | |
SecurityStatus = mSecurity2->FileAuthentication ( | |
mSecurity2, | |
OriginalFilePath, | |
Buffer, | |
Size, | |
FALSE | |
); | |
} | |
// | |
// Verify the Authentication Status through the Security Architectural Protocol | |
// Only on images that have been read using Firmware Volume protocol. | |
// All SMM images are from FV protocol. | |
// | |
if (!EFI_ERROR (SecurityStatus) && (mSecurity != NULL)) { | |
SecurityStatus = mSecurity->FileAuthenticationState ( | |
mSecurity, | |
AuthenticationStatus, | |
OriginalFilePath | |
); | |
} | |
if (EFI_ERROR (SecurityStatus) && (SecurityStatus != EFI_SECURITY_VIOLATION)) { | |
Status = SecurityStatus; | |
return Status; | |
} | |
// | |
// Initialize ImageContext | |
// | |
ImageContext.Handle = Buffer; | |
ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; | |
// | |
// Get information about the image being loaded | |
// | |
Status = PeCoffLoaderGetImageInfo (&ImageContext); | |
if (EFI_ERROR (Status)) { | |
if (Buffer != NULL) { | |
gBS->FreePool (Buffer); | |
} | |
return Status; | |
} | |
// | |
// if Loading module at Fixed Address feature is enabled, then cut out a memory range started from TESG BASE | |
// to hold the Smm driver code | |
// | |
if (PcdGet64 (PcdLoadModuleAtFixAddressEnable) != 0) { | |
// | |
// Get the fixed loading address assigned by Build tool | |
// | |
Status = GetPeCoffImageFixLoadingAssignedAddress (&ImageContext); | |
if (!EFI_ERROR (Status)) { | |
// | |
// Since the memory range to load Smm core already been cut out, so no need to allocate and free this range | |
// following statements is to bypass SmmFreePages | |
// | |
PageCount = 0; | |
DstBuffer = (UINTN)gLoadModuleAtFixAddressSmramBase; | |
} else { | |
DEBUG ((DEBUG_INFO|DEBUG_LOAD, "LOADING MODULE FIXED ERROR: Failed to load module at fixed address. \n")); | |
// | |
// allocate the memory to load the SMM driver | |
// | |
PageCount = (UINTN)EFI_SIZE_TO_PAGES ((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment); | |
DstBuffer = (UINTN)(-1); | |
Status = SmmAllocatePages ( | |
AllocateMaxAddress, | |
EfiRuntimeServicesCode, | |
PageCount, | |
&DstBuffer | |
); | |
if (EFI_ERROR (Status)) { | |
if (Buffer != NULL) { | |
gBS->FreePool (Buffer); | |
} | |
return Status; | |
} | |
ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer; | |
} | |
} else { | |
PageCount = (UINTN)EFI_SIZE_TO_PAGES ((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment); | |
DstBuffer = (UINTN)(-1); | |
Status = SmmAllocatePages ( | |
AllocateMaxAddress, | |
EfiRuntimeServicesCode, | |
PageCount, | |
&DstBuffer | |
); | |
if (EFI_ERROR (Status)) { | |
if (Buffer != NULL) { | |
gBS->FreePool (Buffer); | |
} | |
return Status; | |
} | |
ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer; | |
} | |
// | |
// Align buffer on section boundary | |
// | |
ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; | |
ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)ImageContext.SectionAlignment - 1); | |
// | |
// Load the image to our new buffer | |
// | |
Status = PeCoffLoaderLoadImage (&ImageContext); | |
if (EFI_ERROR (Status)) { | |
if (Buffer != NULL) { | |
gBS->FreePool (Buffer); | |
} | |
SmmFreePages (DstBuffer, PageCount); | |
return Status; | |
} | |
// | |
// Relocate the image in our new buffer | |
// | |
Status = PeCoffLoaderRelocateImage (&ImageContext); | |
if (EFI_ERROR (Status)) { | |
if (Buffer != NULL) { | |
gBS->FreePool (Buffer); | |
} | |
SmmFreePages (DstBuffer, PageCount); | |
return Status; | |
} | |
// | |
// Flush the instruction cache so the image data are written before we execute it | |
// | |
InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); | |
// | |
// Save Image EntryPoint in DriverEntry | |
// | |
DriverEntry->ImageEntryPoint = ImageContext.EntryPoint; | |
DriverEntry->ImageBuffer = DstBuffer; | |
DriverEntry->NumberOfPage = PageCount; | |
// | |
// Allocate a Loaded Image Protocol in EfiBootServicesData | |
// | |
Status = gBS->AllocatePool (EfiBootServicesData, sizeof (EFI_LOADED_IMAGE_PROTOCOL), (VOID **)&DriverEntry->LoadedImage); | |
if (EFI_ERROR (Status)) { | |
if (Buffer != NULL) { | |
gBS->FreePool (Buffer); | |
} | |
SmmFreePages (DstBuffer, PageCount); | |
return Status; | |
} | |
ZeroMem (DriverEntry->LoadedImage, sizeof (EFI_LOADED_IMAGE_PROTOCOL)); | |
// | |
// Fill in the remaining fields of the Loaded Image Protocol instance. | |
// Note: ImageBase is an SMRAM address that can not be accessed outside of SMRAM if SMRAM window is closed. | |
// | |
DriverEntry->LoadedImage->Revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION; | |
DriverEntry->LoadedImage->ParentHandle = gSmmCorePrivate->SmmIplImageHandle; | |
DriverEntry->LoadedImage->SystemTable = gST; | |
DriverEntry->LoadedImage->DeviceHandle = DeviceHandle; | |
DriverEntry->SmmLoadedImage.Revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION; | |
DriverEntry->SmmLoadedImage.ParentHandle = gSmmCorePrivate->SmmIplImageHandle; | |
DriverEntry->SmmLoadedImage.SystemTable = gST; | |
DriverEntry->SmmLoadedImage.DeviceHandle = DeviceHandle; | |
// | |
// Make an EfiBootServicesData buffer copy of FilePath | |
// | |
Status = gBS->AllocatePool (EfiBootServicesData, GetDevicePathSize (FilePath), (VOID **)&DriverEntry->LoadedImage->FilePath); | |
if (EFI_ERROR (Status)) { | |
if (Buffer != NULL) { | |
gBS->FreePool (Buffer); | |
} | |
SmmFreePages (DstBuffer, PageCount); | |
return Status; | |
} | |
CopyMem (DriverEntry->LoadedImage->FilePath, FilePath, GetDevicePathSize (FilePath)); | |
DriverEntry->LoadedImage->ImageBase = (VOID *)(UINTN)ImageContext.ImageAddress; | |
DriverEntry->LoadedImage->ImageSize = ImageContext.ImageSize; | |
DriverEntry->LoadedImage->ImageCodeType = EfiRuntimeServicesCode; | |
DriverEntry->LoadedImage->ImageDataType = EfiRuntimeServicesData; | |
// | |
// Make a buffer copy of FilePath | |
// | |
Status = SmmAllocatePool (EfiRuntimeServicesData, GetDevicePathSize (FilePath), (VOID **)&DriverEntry->SmmLoadedImage.FilePath); | |
if (EFI_ERROR (Status)) { | |
if (Buffer != NULL) { | |
gBS->FreePool (Buffer); | |
} | |
gBS->FreePool (DriverEntry->LoadedImage->FilePath); | |
SmmFreePages (DstBuffer, PageCount); | |
return Status; | |
} | |
CopyMem (DriverEntry->SmmLoadedImage.FilePath, FilePath, GetDevicePathSize (FilePath)); | |
DriverEntry->SmmLoadedImage.ImageBase = (VOID *)(UINTN)ImageContext.ImageAddress; | |
DriverEntry->SmmLoadedImage.ImageSize = ImageContext.ImageSize; | |
DriverEntry->SmmLoadedImage.ImageCodeType = EfiRuntimeServicesCode; | |
DriverEntry->SmmLoadedImage.ImageDataType = EfiRuntimeServicesData; | |
// | |
// Create a new image handle in the UEFI handle database for the SMM Driver | |
// | |
DriverEntry->ImageHandle = NULL; | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&DriverEntry->ImageHandle, | |
&gEfiLoadedImageProtocolGuid, | |
DriverEntry->LoadedImage, | |
NULL | |
); | |
// | |
// Create a new image handle in the SMM handle database for the SMM Driver | |
// | |
DriverEntry->SmmImageHandle = NULL; | |
Status = SmmInstallProtocolInterface ( | |
&DriverEntry->SmmImageHandle, | |
&gEfiLoadedImageProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&DriverEntry->SmmLoadedImage | |
); | |
PERF_LOAD_IMAGE_END (DriverEntry->ImageHandle); | |
// | |
// Print the load address and the PDB file name if it is available | |
// | |
DEBUG_CODE_BEGIN (); | |
UINTN Index; | |
UINTN StartIndex; | |
CHAR8 EfiFileName[256]; | |
DEBUG (( | |
DEBUG_INFO | DEBUG_LOAD, | |
"Loading SMM driver at 0x%11p EntryPoint=0x%11p ", | |
(VOID *)(UINTN)ImageContext.ImageAddress, | |
FUNCTION_ENTRY_POINT (ImageContext.EntryPoint) | |
)); | |
// | |
// Print Module Name by Pdb file path. | |
// Windows and Unix style file path are all trimmed correctly. | |
// | |
if (ImageContext.PdbPointer != NULL) { | |
StartIndex = 0; | |
for (Index = 0; ImageContext.PdbPointer[Index] != 0; Index++) { | |
if ((ImageContext.PdbPointer[Index] == '\\') || (ImageContext.PdbPointer[Index] == '/')) { | |
StartIndex = Index + 1; | |
} | |
} | |
// | |
// Copy the PDB file name to our temporary string, and replace .pdb with .efi | |
// The PDB file name is limited in the range of 0~255. | |
// If the length is bigger than 255, trim the redundant characters to avoid overflow in array boundary. | |
// | |
for (Index = 0; Index < sizeof (EfiFileName) - 4; Index++) { | |
EfiFileName[Index] = ImageContext.PdbPointer[Index + StartIndex]; | |
if (EfiFileName[Index] == 0) { | |
EfiFileName[Index] = '.'; | |
} | |
if (EfiFileName[Index] == '.') { | |
EfiFileName[Index + 1] = 'e'; | |
EfiFileName[Index + 2] = 'f'; | |
EfiFileName[Index + 3] = 'i'; | |
EfiFileName[Index + 4] = 0; | |
break; | |
} | |
} | |
if (Index == sizeof (EfiFileName) - 4) { | |
EfiFileName[Index] = 0; | |
} | |
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "%a", EfiFileName)); // &Image->ImageContext.PdbPointer[StartIndex])); | |
} | |
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "\n")); | |
DEBUG_CODE_END (); | |
// | |
// Free buffer allocated by Fv->ReadSection. | |
// | |
// The UEFI Boot Services FreePool() function must be used because Fv->ReadSection | |
// used the UEFI Boot Services AllocatePool() function | |
// | |
Status = gBS->FreePool (Buffer); | |
if (!EFI_ERROR (Status) && EFI_ERROR (SecurityStatus)) { | |
Status = SecurityStatus; | |
} | |
return Status; | |
} | |
/** | |
Preprocess dependency expression and update DriverEntry to reflect the | |
state of Before and After dependencies. If DriverEntry->Before | |
or DriverEntry->After is set it will never be cleared. | |
@param DriverEntry DriverEntry element to update . | |
@retval EFI_SUCCESS It always works. | |
**/ | |
EFI_STATUS | |
SmmPreProcessDepex ( | |
IN EFI_SMM_DRIVER_ENTRY *DriverEntry | |
) | |
{ | |
UINT8 *Iterator; | |
Iterator = DriverEntry->Depex; | |
DriverEntry->Dependent = TRUE; | |
if (*Iterator == EFI_DEP_BEFORE) { | |
DriverEntry->Before = TRUE; | |
} else if (*Iterator == EFI_DEP_AFTER) { | |
DriverEntry->After = TRUE; | |
} | |
if (DriverEntry->Before || DriverEntry->After) { | |
CopyMem (&DriverEntry->BeforeAfterGuid, Iterator + 1, sizeof (EFI_GUID)); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Read Depex and pre-process the Depex for Before and After. If Section Extraction | |
protocol returns an error via ReadSection defer the reading of the Depex. | |
@param DriverEntry Driver to work on. | |
@retval EFI_SUCCESS Depex read and preprocessed | |
@retval EFI_PROTOCOL_ERROR The section extraction protocol returned an error | |
and Depex reading needs to be retried. | |
@retval Error DEPEX not found. | |
**/ | |
EFI_STATUS | |
SmmGetDepexSectionAndPreProccess ( | |
IN EFI_SMM_DRIVER_ENTRY *DriverEntry | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SECTION_TYPE SectionType; | |
UINT32 AuthenticationStatus; | |
EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; | |
Fv = DriverEntry->Fv; | |
// | |
// Grab Depex info, it will never be free'ed. | |
// (Note: DriverEntry->Depex is in DXE memory) | |
// | |
SectionType = EFI_SECTION_SMM_DEPEX; | |
Status = Fv->ReadSection ( | |
DriverEntry->Fv, | |
&DriverEntry->FileName, | |
SectionType, | |
0, | |
&DriverEntry->Depex, | |
(UINTN *)&DriverEntry->DepexSize, | |
&AuthenticationStatus | |
); | |
if (EFI_ERROR (Status)) { | |
if (Status == EFI_PROTOCOL_ERROR) { | |
// | |
// The section extraction protocol failed so set protocol error flag | |
// | |
DriverEntry->DepexProtocolError = TRUE; | |
} else { | |
// | |
// If no Depex assume depend on all architectural protocols | |
// | |
DriverEntry->Depex = NULL; | |
DriverEntry->Dependent = TRUE; | |
DriverEntry->DepexProtocolError = FALSE; | |
} | |
} else { | |
// | |
// Set Before and After state information based on Depex | |
// Driver will be put in Dependent state | |
// | |
SmmPreProcessDepex (DriverEntry); | |
DriverEntry->DepexProtocolError = FALSE; | |
} | |
return Status; | |
} | |
/** | |
This is the main Dispatcher for SMM and it exits when there are no more | |
drivers to run. Drain the mScheduledQueue and load and start a PE | |
image for each driver. Search the mDiscoveredList to see if any driver can | |
be placed on the mScheduledQueue. If no drivers are placed on the | |
mScheduledQueue exit the function. | |
@retval EFI_SUCCESS All of the SMM Drivers that could be dispatched | |
have been run and the SMM Entry Point has been | |
registered. | |
@retval EFI_NOT_READY The SMM Driver that registered the SMM Entry Point | |
was just dispatched. | |
@retval EFI_NOT_FOUND There are no SMM Drivers available to be dispatched. | |
@retval EFI_ALREADY_STARTED The SMM Dispatcher is already running | |
**/ | |
EFI_STATUS | |
SmmDispatcher ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
EFI_SMM_DRIVER_ENTRY *DriverEntry; | |
BOOLEAN ReadyToRun; | |
BOOLEAN PreviousSmmEntryPointRegistered; | |
if (!gRequestDispatch) { | |
return EFI_NOT_FOUND; | |
} | |
if (gDispatcherRunning) { | |
// | |
// If the dispatcher is running don't let it be restarted. | |
// | |
return EFI_ALREADY_STARTED; | |
} | |
gDispatcherRunning = TRUE; | |
do { | |
// | |
// Drain the Scheduled Queue | |
// | |
while (!IsListEmpty (&mScheduledQueue)) { | |
DriverEntry = CR ( | |
mScheduledQueue.ForwardLink, | |
EFI_SMM_DRIVER_ENTRY, | |
ScheduledLink, | |
EFI_SMM_DRIVER_ENTRY_SIGNATURE | |
); | |
// | |
// Load the SMM Driver image into memory. If the Driver was transitioned from | |
// Untrused to Scheduled it would have already been loaded so we may need to | |
// skip the LoadImage | |
// | |
if (DriverEntry->ImageHandle == NULL) { | |
Status = SmmLoadImage (DriverEntry); | |
// | |
// Update the driver state to reflect that it's been loaded | |
// | |
if (EFI_ERROR (Status)) { | |
// | |
// The SMM Driver could not be loaded, and do not attempt to load or start it again. | |
// Take driver from Scheduled to Initialized. | |
// | |
DriverEntry->Initialized = TRUE; | |
DriverEntry->Scheduled = FALSE; | |
RemoveEntryList (&DriverEntry->ScheduledLink); | |
// | |
// If it's an error don't try the StartImage | |
// | |
continue; | |
} | |
} | |
DriverEntry->Scheduled = FALSE; | |
DriverEntry->Initialized = TRUE; | |
RemoveEntryList (&DriverEntry->ScheduledLink); | |
REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( | |
EFI_PROGRESS_CODE, | |
EFI_SOFTWARE_SMM_DRIVER | EFI_SW_PC_INIT_BEGIN, | |
&DriverEntry->ImageHandle, | |
sizeof (DriverEntry->ImageHandle) | |
); | |
// | |
// Cache state of SmmEntryPointRegistered before calling entry point | |
// | |
PreviousSmmEntryPointRegistered = gSmmCorePrivate->SmmEntryPointRegistered; | |
// | |
// For each SMM driver, pass NULL as ImageHandle | |
// | |
RegisterSmramProfileImage (DriverEntry, TRUE); | |
PERF_START_IMAGE_BEGIN (DriverEntry->ImageHandle); | |
Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)DriverEntry->ImageEntryPoint)(DriverEntry->ImageHandle, gST); | |
PERF_START_IMAGE_END (DriverEntry->ImageHandle); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"Error: SMM image at %11p start failed: %r\n", | |
DriverEntry->SmmLoadedImage.ImageBase, | |
Status | |
)); | |
UnregisterSmramProfileImage (DriverEntry, TRUE); | |
SmmFreePages (DriverEntry->ImageBuffer, DriverEntry->NumberOfPage); | |
// | |
// Uninstall LoadedImage | |
// | |
Status = gBS->UninstallProtocolInterface ( | |
DriverEntry->ImageHandle, | |
&gEfiLoadedImageProtocolGuid, | |
DriverEntry->LoadedImage | |
); | |
if (!EFI_ERROR (Status)) { | |
if (DriverEntry->LoadedImage->FilePath != NULL) { | |
gBS->FreePool (DriverEntry->LoadedImage->FilePath); | |
} | |
gBS->FreePool (DriverEntry->LoadedImage); | |
} | |
Status = SmmUninstallProtocolInterface ( | |
DriverEntry->SmmImageHandle, | |
&gEfiLoadedImageProtocolGuid, | |
&DriverEntry->SmmLoadedImage | |
); | |
if (!EFI_ERROR (Status)) { | |
if (DriverEntry->SmmLoadedImage.FilePath != NULL) { | |
SmmFreePool (DriverEntry->SmmLoadedImage.FilePath); | |
} | |
} | |
} | |
REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( | |
EFI_PROGRESS_CODE, | |
EFI_SOFTWARE_SMM_DRIVER | EFI_SW_PC_INIT_END, | |
&DriverEntry->ImageHandle, | |
sizeof (DriverEntry->ImageHandle) | |
); | |
if (!PreviousSmmEntryPointRegistered && gSmmCorePrivate->SmmEntryPointRegistered) { | |
// | |
// Return immediately if the SMM Entry Point was registered by the SMM | |
// Driver that was just dispatched. The SMM IPL will reinvoke the SMM | |
// Core Dispatcher. This is required so SMM Mode may be enabled as soon | |
// as all the dependent SMM Drivers for SMM Mode have been dispatched. | |
// Once the SMM Entry Point has been registered, then SMM Mode will be | |
// used. | |
// | |
gRequestDispatch = TRUE; | |
gDispatcherRunning = FALSE; | |
return EFI_NOT_READY; | |
} | |
} | |
// | |
// Search DriverList for items to place on Scheduled Queue | |
// | |
ReadyToRun = FALSE; | |
for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { | |
DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); | |
if (DriverEntry->DepexProtocolError) { | |
// | |
// If Section Extraction Protocol did not let the Depex be read before retry the read | |
// | |
Status = SmmGetDepexSectionAndPreProccess (DriverEntry); | |
} | |
if (DriverEntry->Dependent) { | |
if (SmmIsSchedulable (DriverEntry)) { | |
SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry); | |
ReadyToRun = TRUE; | |
} | |
} | |
} | |
} while (ReadyToRun); | |
// | |
// If there is no more SMM driver to dispatch, stop the dispatch request | |
// | |
gRequestDispatch = FALSE; | |
for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { | |
DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); | |
if (!DriverEntry->Initialized) { | |
// | |
// We have SMM driver pending to dispatch | |
// | |
gRequestDispatch = TRUE; | |
break; | |
} | |
} | |
gDispatcherRunning = FALSE; | |
return EFI_SUCCESS; | |
} | |
/** | |
Insert InsertedDriverEntry onto the mScheduledQueue. To do this you | |
must add any driver with a before dependency on InsertedDriverEntry first. | |
You do this by recursively calling this routine. After all the Befores are | |
processed you can add InsertedDriverEntry to the mScheduledQueue. | |
Then you can add any driver with an After dependency on InsertedDriverEntry | |
by recursively calling this routine. | |
@param InsertedDriverEntry The driver to insert on the ScheduledLink Queue | |
**/ | |
VOID | |
SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter ( | |
IN EFI_SMM_DRIVER_ENTRY *InsertedDriverEntry | |
) | |
{ | |
LIST_ENTRY *Link; | |
EFI_SMM_DRIVER_ENTRY *DriverEntry; | |
// | |
// Process Before Dependency | |
// | |
for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { | |
DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); | |
if (DriverEntry->Before && DriverEntry->Dependent && (DriverEntry != InsertedDriverEntry)) { | |
DEBUG ((DEBUG_DISPATCH, "Evaluate SMM DEPEX for FFS(%g)\n", &DriverEntry->FileName)); | |
DEBUG ((DEBUG_DISPATCH, " BEFORE FFS(%g) = ", &DriverEntry->BeforeAfterGuid)); | |
if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) { | |
// | |
// Recursively process BEFORE | |
// | |
DEBUG ((DEBUG_DISPATCH, "TRUE\n END\n RESULT = TRUE\n")); | |
SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry); | |
} else { | |
DEBUG ((DEBUG_DISPATCH, "FALSE\n END\n RESULT = FALSE\n")); | |
} | |
} | |
} | |
// | |
// Convert driver from Dependent to Scheduled state | |
// | |
InsertedDriverEntry->Dependent = FALSE; | |
InsertedDriverEntry->Scheduled = TRUE; | |
InsertTailList (&mScheduledQueue, &InsertedDriverEntry->ScheduledLink); | |
// | |
// Process After Dependency | |
// | |
for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { | |
DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); | |
if (DriverEntry->After && DriverEntry->Dependent && (DriverEntry != InsertedDriverEntry)) { | |
DEBUG ((DEBUG_DISPATCH, "Evaluate SMM DEPEX for FFS(%g)\n", &DriverEntry->FileName)); | |
DEBUG ((DEBUG_DISPATCH, " AFTER FFS(%g) = ", &DriverEntry->BeforeAfterGuid)); | |
if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) { | |
// | |
// Recursively process AFTER | |
// | |
DEBUG ((DEBUG_DISPATCH, "TRUE\n END\n RESULT = TRUE\n")); | |
SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry); | |
} else { | |
DEBUG ((DEBUG_DISPATCH, "FALSE\n END\n RESULT = FALSE\n")); | |
} | |
} | |
} | |
} | |
/** | |
Return TRUE if the Fv has been processed, FALSE if not. | |
@param FvHandle The handle of a FV that's being tested | |
@retval TRUE Fv protocol on FvHandle has been processed | |
@retval FALSE Fv protocol on FvHandle has not yet been | |
processed | |
**/ | |
BOOLEAN | |
FvHasBeenProcessed ( | |
IN EFI_HANDLE FvHandle | |
) | |
{ | |
LIST_ENTRY *Link; | |
KNOWN_HANDLE *KnownHandle; | |
for (Link = mFvHandleList.ForwardLink; Link != &mFvHandleList; Link = Link->ForwardLink) { | |
KnownHandle = CR (Link, KNOWN_HANDLE, Link, KNOWN_HANDLE_SIGNATURE); | |
if (KnownHandle->Handle == FvHandle) { | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
/** | |
Remember that Fv protocol on FvHandle has had its drivers placed on the | |
mDiscoveredList. This function adds entries on the mFvHandleList. Items are | |
never removed/freed from the mFvHandleList. | |
@param FvHandle The handle of a FV that has been processed | |
**/ | |
VOID | |
FvIsBeingProcessed ( | |
IN EFI_HANDLE FvHandle | |
) | |
{ | |
KNOWN_HANDLE *KnownHandle; | |
KnownHandle = AllocatePool (sizeof (KNOWN_HANDLE)); | |
ASSERT (KnownHandle != NULL); | |
KnownHandle->Signature = KNOWN_HANDLE_SIGNATURE; | |
KnownHandle->Handle = FvHandle; | |
InsertTailList (&mFvHandleList, &KnownHandle->Link); | |
} | |
/** | |
Convert FvHandle and DriverName into an EFI device path | |
@param Fv Fv protocol, needed to read Depex info out of | |
FLASH. | |
@param FvHandle Handle for Fv, needed in the | |
EFI_SMM_DRIVER_ENTRY so that the PE image can be | |
read out of the FV at a later time. | |
@param DriverName Name of driver to add to mDiscoveredList. | |
@return Pointer to device path constructed from FvHandle and DriverName | |
**/ | |
EFI_DEVICE_PATH_PROTOCOL * | |
SmmFvToDevicePath ( | |
IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv, | |
IN EFI_HANDLE FvHandle, | |
IN EFI_GUID *DriverName | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_DEVICE_PATH_PROTOCOL *FvDevicePath; | |
EFI_DEVICE_PATH_PROTOCOL *FileNameDevicePath; | |
// | |
// Remember the device path of the FV | |
// | |
Status = gBS->HandleProtocol (FvHandle, &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath); | |
if (EFI_ERROR (Status)) { | |
FileNameDevicePath = NULL; | |
} else { | |
// | |
// Build a device path to the file in the FV to pass into gBS->LoadImage | |
// | |
EfiInitializeFwVolDevicepathNode (&mFvDevicePath.File, DriverName); | |
SetDevicePathEndNode (&mFvDevicePath.End); | |
// | |
// Note: FileNameDevicePath is in DXE memory | |
// | |
FileNameDevicePath = AppendDevicePath ( | |
FvDevicePath, | |
(EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath | |
); | |
} | |
return FileNameDevicePath; | |
} | |
/** | |
Add an entry to the mDiscoveredList. Allocate memory to store the DriverEntry, | |
and initialize any state variables. Read the Depex from the FV and store it | |
in DriverEntry. Pre-process the Depex to set the Before and After state. | |
The Discovered list is never free'ed and contains booleans that represent the | |
other possible SMM driver states. | |
@param Fv Fv protocol, needed to read Depex info out of | |
FLASH. | |
@param FvHandle Handle for Fv, needed in the | |
EFI_SMM_DRIVER_ENTRY so that the PE image can be | |
read out of the FV at a later time. | |
@param DriverName Name of driver to add to mDiscoveredList. | |
@retval EFI_SUCCESS If driver was added to the mDiscoveredList. | |
@retval EFI_ALREADY_STARTED The driver has already been started. Only one | |
DriverName may be active in the system at any one | |
time. | |
**/ | |
EFI_STATUS | |
SmmAddToDriverList ( | |
IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv, | |
IN EFI_HANDLE FvHandle, | |
IN EFI_GUID *DriverName | |
) | |
{ | |
EFI_SMM_DRIVER_ENTRY *DriverEntry; | |
// | |
// Create the Driver Entry for the list. ZeroPool initializes lots of variables to | |
// NULL or FALSE. | |
// | |
DriverEntry = AllocateZeroPool (sizeof (EFI_SMM_DRIVER_ENTRY)); | |
ASSERT (DriverEntry != NULL); | |
DriverEntry->Signature = EFI_SMM_DRIVER_ENTRY_SIGNATURE; | |
CopyGuid (&DriverEntry->FileName, DriverName); | |
DriverEntry->FvHandle = FvHandle; | |
DriverEntry->Fv = Fv; | |
DriverEntry->FvFileDevicePath = SmmFvToDevicePath (Fv, FvHandle, DriverName); | |
SmmGetDepexSectionAndPreProccess (DriverEntry); | |
InsertTailList (&mDiscoveredList, &DriverEntry->Link); | |
gRequestDispatch = TRUE; | |
return EFI_SUCCESS; | |
} | |
/** | |
This function is the main entry point for an SMM handler dispatch | |
or communicate-based callback. | |
Event notification that is fired every time a FV dispatch protocol is added. | |
More than one protocol may have been added when this event is fired, so you | |
must loop on SmmLocateHandle () to see how many protocols were added and | |
do the following to each FV: | |
If the Fv has already been processed, skip it. If the Fv has not been | |
processed then mark it as being processed, as we are about to process it. | |
Read the Fv and add any driver in the Fv to the mDiscoveredList.The | |
mDiscoveredList is never free'ed and contains variables that define | |
the other states the SMM driver transitions to.. | |
While you are at it read the A Priori file into memory. | |
Place drivers in the A Priori list onto the mScheduledQueue. | |
@param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). | |
@param Context Points to an optional handler context which was specified when the handler was registered. | |
@param CommBuffer A pointer to a collection of data in memory that will | |
be conveyed from a non-SMM environment into an SMM environment. | |
@param CommBufferSize The size of the CommBuffer. | |
@return Status Code | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmmDriverDispatchHandler ( | |
IN EFI_HANDLE DispatchHandle, | |
IN CONST VOID *Context OPTIONAL, | |
IN OUT VOID *CommBuffer OPTIONAL, | |
IN OUT UINTN *CommBufferSize OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN HandleCount; | |
EFI_HANDLE *HandleBuffer; | |
EFI_STATUS GetNextFileStatus; | |
EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; | |
EFI_DEVICE_PATH_PROTOCOL *FvDevicePath; | |
EFI_HANDLE FvHandle; | |
EFI_GUID NameGuid; | |
UINTN Key; | |
EFI_FV_FILETYPE Type; | |
EFI_FV_FILE_ATTRIBUTES Attributes; | |
UINTN Size; | |
EFI_SMM_DRIVER_ENTRY *DriverEntry; | |
EFI_GUID *AprioriFile; | |
UINTN AprioriEntryCount; | |
UINTN HandleIndex; | |
UINTN SmmTypeIndex; | |
UINTN AprioriIndex; | |
LIST_ENTRY *Link; | |
UINT32 AuthenticationStatus; | |
UINTN SizeOfBuffer; | |
HandleBuffer = NULL; | |
Status = gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiFirmwareVolume2ProtocolGuid, | |
NULL, | |
&HandleCount, | |
&HandleBuffer | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_NOT_FOUND; | |
} | |
PERF_CALLBACK_BEGIN (&gEfiEventDxeDispatchGuid); | |
for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) { | |
FvHandle = HandleBuffer[HandleIndex]; | |
if (FvHasBeenProcessed (FvHandle)) { | |
// | |
// This Fv has already been processed so lets skip it! | |
// | |
continue; | |
} | |
// | |
// Since we are about to process this Fv mark it as processed. | |
// | |
FvIsBeingProcessed (FvHandle); | |
Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv); | |
if (EFI_ERROR (Status)) { | |
// | |
// FvHandle must have a Firmware Volume2 Protocol thus we should never get here. | |
// | |
ASSERT (FALSE); | |
continue; | |
} | |
Status = gBS->HandleProtocol (FvHandle, &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath); | |
if (EFI_ERROR (Status)) { | |
// | |
// The Firmware volume doesn't have device path, can't be dispatched. | |
// | |
continue; | |
} | |
// | |
// Discover Drivers in FV and add them to the Discovered Driver List. | |
// Process EFI_FV_FILETYPE_SMM type and then EFI_FV_FILETYPE_COMBINED_SMM_DXE | |
// EFI_FV_FILETYPE_SMM_CORE is processed to produce a Loaded Image protocol for the core | |
// | |
for (SmmTypeIndex = 0; SmmTypeIndex < sizeof (mSmmFileTypes)/sizeof (EFI_FV_FILETYPE); SmmTypeIndex++) { | |
// | |
// Initialize the search key | |
// | |
Key = 0; | |
do { | |
Type = mSmmFileTypes[SmmTypeIndex]; | |
GetNextFileStatus = Fv->GetNextFile ( | |
Fv, | |
&Key, | |
&Type, | |
&NameGuid, | |
&Attributes, | |
&Size | |
); | |
if (!EFI_ERROR (GetNextFileStatus)) { | |
if (Type == EFI_FV_FILETYPE_SMM_CORE) { | |
// | |
// If this is the SMM core fill in it's DevicePath & DeviceHandle | |
// | |
if (mSmmCoreLoadedImage->FilePath == NULL) { | |
// | |
// Maybe one special FV contains only one SMM_CORE module, so its device path must | |
// be initialized completely. | |
// | |
EfiInitializeFwVolDevicepathNode (&mFvDevicePath.File, &NameGuid); | |
SetDevicePathEndNode (&mFvDevicePath.End); | |
// | |
// Make an EfiBootServicesData buffer copy of FilePath | |
// | |
Status = gBS->AllocatePool ( | |
EfiBootServicesData, | |
GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath), | |
(VOID **)&mSmmCoreLoadedImage->FilePath | |
); | |
ASSERT_EFI_ERROR (Status); | |
CopyMem (mSmmCoreLoadedImage->FilePath, &mFvDevicePath, GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath)); | |
mSmmCoreLoadedImage->DeviceHandle = FvHandle; | |
} | |
if (mSmmCoreDriverEntry->SmmLoadedImage.FilePath == NULL) { | |
// | |
// Maybe one special FV contains only one SMM_CORE module, so its device path must | |
// be initialized completely. | |
// | |
EfiInitializeFwVolDevicepathNode (&mFvDevicePath.File, &NameGuid); | |
SetDevicePathEndNode (&mFvDevicePath.End); | |
// | |
// Make a buffer copy FilePath | |
// | |
Status = SmmAllocatePool ( | |
EfiRuntimeServicesData, | |
GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath), | |
(VOID **)&mSmmCoreDriverEntry->SmmLoadedImage.FilePath | |
); | |
ASSERT_EFI_ERROR (Status); | |
CopyMem (mSmmCoreDriverEntry->SmmLoadedImage.FilePath, &mFvDevicePath, GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath)); | |
mSmmCoreDriverEntry->SmmLoadedImage.DeviceHandle = FvHandle; | |
} | |
} else { | |
SmmAddToDriverList (Fv, FvHandle, &NameGuid); | |
} | |
} | |
} while (!EFI_ERROR (GetNextFileStatus)); | |
} | |
// | |
// Read the array of GUIDs from the Apriori file if it is present in the firmware volume | |
// (Note: AprioriFile is in DXE memory) | |
// | |
AprioriFile = NULL; | |
Status = Fv->ReadSection ( | |
Fv, | |
&gAprioriGuid, | |
EFI_SECTION_RAW, | |
0, | |
(VOID **)&AprioriFile, | |
&SizeOfBuffer, | |
&AuthenticationStatus | |
); | |
if (!EFI_ERROR (Status)) { | |
AprioriEntryCount = SizeOfBuffer / sizeof (EFI_GUID); | |
} else { | |
AprioriEntryCount = 0; | |
} | |
// | |
// Put drivers on Apriori List on the Scheduled queue. The Discovered List includes | |
// drivers not in the current FV and these must be skipped since the a priori list | |
// is only valid for the FV that it resided in. | |
// | |
for (AprioriIndex = 0; AprioriIndex < AprioriEntryCount; AprioriIndex++) { | |
for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { | |
DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); | |
if (CompareGuid (&DriverEntry->FileName, &AprioriFile[AprioriIndex]) && | |
(FvHandle == DriverEntry->FvHandle)) | |
{ | |
DriverEntry->Dependent = FALSE; | |
DriverEntry->Scheduled = TRUE; | |
InsertTailList (&mScheduledQueue, &DriverEntry->ScheduledLink); | |
DEBUG ((DEBUG_DISPATCH, "Evaluate SMM DEPEX for FFS(%g)\n", &DriverEntry->FileName)); | |
DEBUG ((DEBUG_DISPATCH, " RESULT = TRUE (Apriori)\n")); | |
break; | |
} | |
} | |
} | |
// | |
// Free data allocated by Fv->ReadSection () | |
// | |
// The UEFI Boot Services FreePool() function must be used because Fv->ReadSection | |
// used the UEFI Boot Services AllocatePool() function | |
// | |
gBS->FreePool (AprioriFile); | |
} | |
// | |
// Execute the SMM Dispatcher on any newly discovered FVs and previously | |
// discovered SMM drivers that have been discovered but not dispatched. | |
// | |
Status = SmmDispatcher (); | |
// | |
// Check to see if CommBuffer and CommBufferSize are valid | |
// | |
if ((CommBuffer != NULL) && (CommBufferSize != NULL)) { | |
if (*CommBufferSize > 0) { | |
if (Status == EFI_NOT_READY) { | |
// | |
// If a the SMM Core Entry Point was just registered, then set flag to | |
// request the SMM Dispatcher to be restarted. | |
// | |
*(UINT8 *)CommBuffer = COMM_BUFFER_SMM_DISPATCH_RESTART; | |
} else if (!EFI_ERROR (Status)) { | |
// | |
// Set the flag to show that the SMM Dispatcher executed without errors | |
// | |
*(UINT8 *)CommBuffer = COMM_BUFFER_SMM_DISPATCH_SUCCESS; | |
} else { | |
// | |
// Set the flag to show that the SMM Dispatcher encountered an error | |
// | |
*(UINT8 *)CommBuffer = COMM_BUFFER_SMM_DISPATCH_ERROR; | |
} | |
} | |
} | |
PERF_CALLBACK_END (&gEfiEventDxeDispatchGuid); | |
return EFI_SUCCESS; | |
} | |
/** | |
Traverse the discovered list for any drivers that were discovered but not loaded | |
because the dependency expressions evaluated to false. | |
**/ | |
VOID | |
SmmDisplayDiscoveredNotDispatched ( | |
VOID | |
) | |
{ | |
LIST_ENTRY *Link; | |
EFI_SMM_DRIVER_ENTRY *DriverEntry; | |
for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { | |
DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE); | |
if (DriverEntry->Dependent) { | |
DEBUG ((DEBUG_LOAD, "SMM Driver %g was discovered but not loaded!!\n", &DriverEntry->FileName)); | |
} | |
} | |
} |