summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Core/PiSmmCore/Dispatcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'MdeModulePkg/Core/PiSmmCore/Dispatcher.c')
-rw-r--r--MdeModulePkg/Core/PiSmmCore/Dispatcher.c3078
1 files changed, 1539 insertions, 1539 deletions
diff --git a/MdeModulePkg/Core/PiSmmCore/Dispatcher.c b/MdeModulePkg/Core/PiSmmCore/Dispatcher.c
index bb789e5890..cc2c29ff03 100644
--- a/MdeModulePkg/Core/PiSmmCore/Dispatcher.c
+++ b/MdeModulePkg/Core/PiSmmCore/Dispatcher.c
@@ -1,1539 +1,1539 @@
-/** @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));
- }
- }
-}
+/** @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));
+ }
+ }
+}