| /** @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)); | |
| } | |
| } | |
| } |