+/** @file\r
+ SMM Driver Dispatcher.\r
+\r
+ Step #1 - When a FV protocol is added to the system every driver in the FV\r
+ is added to the mDiscoveredList. The SOR, Before, and After Depex are\r
+ pre-processed as drivers are added to the mDiscoveredList. If an Apriori\r
+ file exists in the FV those drivers are addeded to the\r
+ mScheduledQueue. The mFvHandleList is used to make sure a\r
+ FV is only processed once.\r
+\r
+ Step #2 - Dispatch. Remove driver from the mScheduledQueue and load and\r
+ start it. After mScheduledQueue is drained check the\r
+ mDiscoveredList to see if any item has a Depex that is ready to\r
+ be placed on the mScheduledQueue.\r
+\r
+ Step #3 - Adding to the mScheduledQueue requires that you process Before\r
+ and After dependencies. This is done recursively as the call to add\r
+ to the mScheduledQueue checks for Before and recursively adds\r
+ all Befores. It then addes the item that was passed in and then\r
+ processess the After dependecies by recursively calling the routine.\r
+\r
+ Dispatcher Rules:\r
+ The rules for the dispatcher are similar to the DXE dispatcher.\r
+\r
+ The rules for DXE dispatcher are in chapter 10 of the DXE CIS. Figure 10-3\r
+ is the state diagram for the DXE dispatcher\r
+\r
+ Depex - Dependency Expresion.\r
+ SOR - Schedule On Request - Don't schedule if this bit is set.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+ This program and the accompanying materials are licensed and made available \r
+ under the terms and conditions of the BSD License which accompanies this \r
+ distribution. The full text of the license may be found at \r
+ http://opensource.org/licenses/bsd-license.php \r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
+\r
+**/\r
+\r
+#include "PiSmmCore.h"\r
+\r
+//\r
+// SMM Dispatcher Data structures\r
+//\r
+#define KNOWN_HANDLE_SIGNATURE SIGNATURE_32('k','n','o','w')\r
+typedef struct {\r
+ UINTN Signature;\r
+ LIST_ENTRY Link; // mFvHandleList\r
+ EFI_HANDLE Handle;\r
+} KNOWN_HANDLE;\r
+\r
+//\r
+// Function Prototypes\r
+//\r
+\r
+/**\r
+ Insert InsertedDriverEntry onto the mScheduledQueue. To do this you\r
+ must add any driver with a before dependency on InsertedDriverEntry first.\r
+ You do this by recursively calling this routine. After all the Befores are\r
+ processed you can add InsertedDriverEntry to the mScheduledQueue.\r
+ Then you can add any driver with an After dependency on InsertedDriverEntry\r
+ by recursively calling this routine.\r
+\r
+ @param InsertedDriverEntry The driver to insert on the ScheduledLink Queue\r
+\r
+**/\r
+VOID\r
+SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (\r
+ IN EFI_SMM_DRIVER_ENTRY *InsertedDriverEntry\r
+ );\r
+\r
+//\r
+// The Driver List contains one copy of every driver that has been discovered.\r
+// Items are never removed from the driver list. List of EFI_SMM_DRIVER_ENTRY\r
+//\r
+LIST_ENTRY mDiscoveredList = INITIALIZE_LIST_HEAD_VARIABLE (mDiscoveredList);\r
+\r
+//\r
+// Queue of drivers that are ready to dispatch. This queue is a subset of the\r
+// mDiscoveredList.list of EFI_SMM_DRIVER_ENTRY.\r
+//\r
+LIST_ENTRY mScheduledQueue = INITIALIZE_LIST_HEAD_VARIABLE (mScheduledQueue);\r
+\r
+//\r
+// List of handles who's Fv's have been parsed and added to the mFwDriverList.\r
+//\r
+LIST_ENTRY mFvHandleList = INITIALIZE_LIST_HEAD_VARIABLE (mFvHandleList);\r
+\r
+//\r
+// Flag for the SMM Dispacher. TRUE if dispatcher is execuing.\r
+//\r
+BOOLEAN gDispatcherRunning = FALSE;\r
+\r
+//\r
+// Flag for the SMM Dispacher. TRUE if there is one or more SMM drivers ready to be dispatched\r
+//\r
+BOOLEAN gRequestDispatch = FALSE;\r
+\r
+//\r
+// List of file types supported by dispatcher\r
+//\r
+EFI_FV_FILETYPE mSmmFileTypes[] = {\r
+ EFI_FV_FILETYPE_SMM,\r
+ EFI_FV_FILETYPE_COMBINED_SMM_DXE\r
+ //\r
+ // Note: DXE core will process the FV image file, so skip it in SMM core\r
+ // EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE\r
+ //\r
+};\r
+\r
+typedef struct {\r
+ MEDIA_FW_VOL_FILEPATH_DEVICE_PATH File;\r
+ EFI_DEVICE_PATH_PROTOCOL End;\r
+} FV_FILEPATH_DEVICE_PATH;\r
+\r
+FV_FILEPATH_DEVICE_PATH mFvDevicePath;\r
+\r
+//\r
+// DXE Architecture Protocols\r
+//\r
+EFI_SECURITY_ARCH_PROTOCOL *mSecurity = NULL;\r
+\r
+/**\r
+ Loads an EFI image into SMRAM.\r
+\r
+ @param DriverEntry EFI_SMM_DRIVER_ENTRY instance\r
+\r
+ @return EFI_STATUS\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmLoadImage (\r
+ IN OUT EFI_SMM_DRIVER_ENTRY *DriverEntry\r
+ )\r
+{\r
+ UINT32 AuthenticationStatus;\r
+ UINTN FilePathSize;\r
+ VOID *Buffer;\r
+ UINTN Size;\r
+ UINTN PageCount;\r
+ EFI_GUID *NameGuid;\r
+ EFI_STATUS Status;\r
+ EFI_STATUS SecurityStatus;\r
+ EFI_HANDLE DeviceHandle;\r
+ EFI_PHYSICAL_ADDRESS DstBuffer;\r
+ EFI_DEVICE_PATH_PROTOCOL *FilePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *OriginalFilePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *HandleFilePath;\r
+ EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;\r
+ PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;\r
+ \r
+ Buffer = NULL;\r
+ Size = 0;\r
+ Fv = DriverEntry->Fv;\r
+ NameGuid = &DriverEntry->FileName;\r
+ FilePath = DriverEntry->FvFileDevicePath;\r
+\r
+ OriginalFilePath = FilePath;\r
+ HandleFilePath = FilePath;\r
+ DeviceHandle = NULL;\r
+ SecurityStatus = EFI_SUCCESS;\r
+ Status = EFI_SUCCESS;\r
+ AuthenticationStatus = 0;\r
+\r
+ //\r
+ // Try to get the image device handle by checking the match protocol.\r
+ //\r
+ Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &HandleFilePath, &DeviceHandle);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // If the Security Architectural Protocol has not been located yet, then attempt to locate it\r
+ //\r
+ if (mSecurity == NULL) {\r
+ gBS->LocateProtocol (&gEfiSecurityArchProtocolGuid, NULL, (VOID**)&mSecurity);\r
+ }\r
+\r
+ //\r
+ // Verify the Authentication Status through the Security Architectural Protocol\r
+ //\r
+ if ((mSecurity != NULL) && (OriginalFilePath != NULL)) {\r
+ SecurityStatus = mSecurity->FileAuthenticationState (\r
+ mSecurity,\r
+ AuthenticationStatus,\r
+ OriginalFilePath\r
+ );\r
+ if (EFI_ERROR (SecurityStatus) && SecurityStatus != EFI_SECURITY_VIOLATION) {\r
+ Status = SecurityStatus;\r
+ return Status;\r
+ }\r
+ }\r
+ \r
+ //\r
+ // Pull out just the file portion of the DevicePath for the LoadedImage FilePath\r
+ //\r
+ FilePath = OriginalFilePath;\r
+ Status = gBS->HandleProtocol (DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID **)&HandleFilePath);\r
+ if (!EFI_ERROR (Status)) {\r
+ FilePathSize = GetDevicePathSize (HandleFilePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL);\r
+ FilePath = (EFI_DEVICE_PATH_PROTOCOL *) (((UINT8 *)FilePath) + FilePathSize );\r
+ }\r
+\r
+ //\r
+ // Try reading PE32 section firstly\r
+ //\r
+ Status = Fv->ReadSection (\r
+ Fv,\r
+ NameGuid,\r
+ EFI_SECTION_PE32,\r
+ 0,\r
+ &Buffer,\r
+ &Size,\r
+ &AuthenticationStatus\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Try reading TE section secondly\r
+ //\r
+ Buffer = NULL;\r
+ Size = 0;\r
+ Status = Fv->ReadSection (\r
+ Fv,\r
+ NameGuid,\r
+ EFI_SECTION_TE,\r
+ 0,\r
+ &Buffer,\r
+ &Size,\r
+ &AuthenticationStatus\r
+ );\r
+ }\r
+ \r
+ if (EFI_ERROR (Status)) {\r
+ if (Buffer != NULL) {\r
+ Status = gBS->FreePool (Buffer);\r
+ }\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Initialize ImageContext\r
+ //\r
+ ImageContext.Handle = Buffer;\r
+ ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;\r
+\r
+ //\r
+ // Get information about the image being loaded\r
+ //\r
+ Status = PeCoffLoaderGetImageInfo (&ImageContext);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Buffer != NULL) {\r
+ Status = gBS->FreePool (Buffer);\r
+ }\r
+ return Status;\r
+ }\r
+\r
+ PageCount = (UINTN)EFI_SIZE_TO_PAGES(ImageContext.ImageSize + ImageContext.SectionAlignment);\r
+ DstBuffer = (UINTN)(-1);\r
+ \r
+ Status = SmmAllocatePages (\r
+ AllocateMaxAddress,\r
+ EfiRuntimeServicesCode,\r
+ PageCount,\r
+ &DstBuffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ if (Buffer != NULL) {\r
+ Status = gBS->FreePool (Buffer);\r
+ }\r
+ return Status;\r
+ }\r
+\r
+ ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer;\r
+ //\r
+ // Align buffer on section boundry\r
+ //\r
+ ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;\r
+ ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1);\r
+\r
+ //\r
+ // Load the image to our new buffer\r
+ //\r
+ Status = PeCoffLoaderLoadImage (&ImageContext);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Buffer != NULL) {\r
+ Status = gBS->FreePool (Buffer);\r
+ }\r
+ SmmFreePages (DstBuffer, PageCount);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Relocate the image in our new buffer\r
+ //\r
+ Status = PeCoffLoaderRelocateImage (&ImageContext);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Buffer != NULL) {\r
+ Status = gBS->FreePool (Buffer);\r
+ }\r
+ SmmFreePages (DstBuffer, PageCount);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Flush the instruction cache so the image data are written before we execute it\r
+ //\r
+ InvalidateInstructionCacheRange ((VOID *)(UINTN) ImageContext.ImageAddress, (UINTN) ImageContext.ImageSize);\r
+\r
+ //\r
+ // Save Image EntryPoint in DriverEntry\r
+ //\r
+ DriverEntry->ImageEntryPoint = ImageContext.EntryPoint;\r
+ DriverEntry->ImageBuffer = DstBuffer; \r
+ DriverEntry->NumberOfPage = PageCount;\r
+\r
+ //\r
+ // Allocate a Loaded Image Protocol in EfiBootServicesData\r
+ //\r
+ Status = gBS->AllocatePool (EfiBootServicesData, sizeof (EFI_LOADED_IMAGE_PROTOCOL), (VOID **)&DriverEntry->LoadedImage);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Buffer != NULL) {\r
+ Status = gBS->FreePool (Buffer);\r
+ }\r
+ SmmFreePages (DstBuffer, PageCount);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Fill in the remaining fields of the Loaded Image Protocol instance.\r
+ // Note: ImageBase is an SMRAM address that can not be accessed outside of SMRAM if SMRAM window is closed.\r
+ //\r
+ DriverEntry->LoadedImage->Revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION;\r
+ DriverEntry->LoadedImage->ParentHandle = gSmmCorePrivate->SmmIplImageHandle;\r
+ DriverEntry->LoadedImage->SystemTable = gST;\r
+ DriverEntry->LoadedImage->DeviceHandle = DeviceHandle;\r
+\r
+ //\r
+ // Make an EfiBootServicesData buffer copy of FilePath\r
+ //\r
+ Status = gBS->AllocatePool (EfiBootServicesData, GetDevicePathSize (FilePath), (VOID **)&DriverEntry->LoadedImage->FilePath);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Buffer != NULL) {\r
+ Status = gBS->FreePool (Buffer);\r
+ }\r
+ SmmFreePages (DstBuffer, PageCount);\r
+ return Status;\r
+ }\r
+ CopyMem (DriverEntry->LoadedImage->FilePath, FilePath, GetDevicePathSize (FilePath));\r
+\r
+ DriverEntry->LoadedImage->ImageBase = (VOID *)(UINTN)DriverEntry->ImageBuffer;\r
+ DriverEntry->LoadedImage->ImageSize = ImageContext.ImageSize;\r
+ DriverEntry->LoadedImage->ImageCodeType = EfiRuntimeServicesCode;\r
+ DriverEntry->LoadedImage->ImageDataType = EfiRuntimeServicesData;\r
+\r
+ //\r
+ // Create a new image handle in the UEFI handle database for the SMM Driver\r
+ //\r
+ DriverEntry->ImageHandle = NULL;\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &DriverEntry->ImageHandle,\r
+ &gEfiLoadedImageProtocolGuid, DriverEntry->LoadedImage,\r
+ NULL\r
+ );\r
+\r
+ //\r
+ // Print the load address and the PDB file name if it is available\r
+ //\r
+\r
+ DEBUG_CODE_BEGIN ();\r
+\r
+ UINTN Index;\r
+ UINTN StartIndex;\r
+ CHAR8 EfiFileName[256];\r
+\r
+\r
+ DEBUG ((DEBUG_INFO | DEBUG_LOAD,\r
+ "Loading driver at 0x%11p EntryPoint=0x%11p ",\r
+ (VOID *)(UINTN) ImageContext.ImageAddress,\r
+ FUNCTION_ENTRY_POINT (ImageContext.EntryPoint)));\r
+\r
+\r
+ //\r
+ // Print Module Name by Pdb file path.\r
+ // Windows and Unix style file path are all trimmed correctly.\r
+ //\r
+ if (ImageContext.PdbPointer != NULL) {\r
+ StartIndex = 0;\r
+ for (Index = 0; ImageContext.PdbPointer[Index] != 0; Index++) {\r
+ if ((ImageContext.PdbPointer[Index] == '\\') || (ImageContext.PdbPointer[Index] == '/')) {\r
+ StartIndex = Index + 1;\r
+ }\r
+ }\r
+ //\r
+ // Copy the PDB file name to our temporary string, and replace .pdb with .efi\r
+ // The PDB file name is limited in the range of 0~255.\r
+ // If the length is bigger than 255, trim the redudant characters to avoid overflow in array boundary.\r
+ //\r
+ for (Index = 0; Index < sizeof (EfiFileName) - 4; Index++) {\r
+ EfiFileName[Index] = ImageContext.PdbPointer[Index + StartIndex];\r
+ if (EfiFileName[Index] == 0) {\r
+ EfiFileName[Index] = '.';\r
+ }\r
+ if (EfiFileName[Index] == '.') {\r
+ EfiFileName[Index + 1] = 'e';\r
+ EfiFileName[Index + 2] = 'f';\r
+ EfiFileName[Index + 3] = 'i';\r
+ EfiFileName[Index + 4] = 0;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Index == sizeof (EfiFileName) - 4) {\r
+ EfiFileName[Index] = 0;\r
+ }\r
+ DEBUG ((DEBUG_INFO | DEBUG_LOAD, "%a", EfiFileName)); // &Image->ImageContext.PdbPointer[StartIndex]));\r
+ }\r
+ DEBUG ((DEBUG_INFO | DEBUG_LOAD, "\n"));\r
+\r
+ DEBUG_CODE_END ();\r
+\r
+ //\r
+ // Free buffer allocated by Fv->ReadSection.\r
+ //\r
+ // The UEFI Boot Services FreePool() function must be used because Fv->ReadSection \r
+ // used the UEFI Boot Services AllocatePool() function\r
+ //\r
+ Status = gBS->FreePool(Buffer);\r
+ return Status; \r
+}\r
+\r
+/**\r
+ Preprocess dependency expression and update DriverEntry to reflect the\r
+ state of Before, After, and SOR dependencies. If DriverEntry->Before\r
+ or DriverEntry->After is set it will never be cleared. If SOR is set\r
+ it will be cleared by SmmSchedule(), and then the driver can be\r
+ dispatched.\r
+\r
+ @param DriverEntry DriverEntry element to update .\r
+\r
+ @retval EFI_SUCCESS It always works.\r
+\r
+**/\r
+EFI_STATUS\r
+SmmPreProcessDepex (\r
+ IN EFI_SMM_DRIVER_ENTRY *DriverEntry\r
+ )\r
+{\r
+ UINT8 *Iterator;\r
+\r
+ Iterator = DriverEntry->Depex;\r
+ if (*Iterator == EFI_DEP_SOR) {\r
+ DriverEntry->Unrequested = TRUE;\r
+ } else {\r
+ DriverEntry->Dependent = TRUE;\r
+ }\r
+\r
+ if (*Iterator == EFI_DEP_BEFORE) {\r
+ DriverEntry->Before = TRUE;\r
+ } else if (*Iterator == EFI_DEP_AFTER) {\r
+ DriverEntry->After = TRUE;\r
+ }\r
+\r
+ if (DriverEntry->Before || DriverEntry->After) {\r
+ CopyMem (&DriverEntry->BeforeAfterGuid, Iterator + 1, sizeof (EFI_GUID));\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Read Depex and pre-process the Depex for Before and After. If Section Extraction\r
+ protocol returns an error via ReadSection defer the reading of the Depex.\r
+\r
+ @param DriverEntry Driver to work on.\r
+\r
+ @retval EFI_SUCCESS Depex read and preprossesed\r
+ @retval EFI_PROTOCOL_ERROR The section extraction protocol returned an error\r
+ and Depex reading needs to be retried.\r
+ @retval Error DEPEX not found.\r
+\r
+**/\r
+EFI_STATUS\r
+SmmGetDepexSectionAndPreProccess (\r
+ IN EFI_SMM_DRIVER_ENTRY *DriverEntry\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_SECTION_TYPE SectionType;\r
+ UINT32 AuthenticationStatus;\r
+ EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;\r
+\r
+ Fv = DriverEntry->Fv;\r
+\r
+ //\r
+ // Grab Depex info, it will never be free'ed.\r
+ // (Note: DriverEntry->Depex is in DXE memory)\r
+ //\r
+ SectionType = EFI_SECTION_SMM_DEPEX;\r
+ Status = Fv->ReadSection (\r
+ DriverEntry->Fv,\r
+ &DriverEntry->FileName,\r
+ SectionType,\r
+ 0,\r
+ &DriverEntry->Depex,\r
+ (UINTN *)&DriverEntry->DepexSize,\r
+ &AuthenticationStatus\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_PROTOCOL_ERROR) {\r
+ //\r
+ // The section extraction protocol failed so set protocol error flag\r
+ //\r
+ DriverEntry->DepexProtocolError = TRUE;\r
+ } else {\r
+ //\r
+ // If no Depex assume depend on all architectural protocols\r
+ //\r
+ DriverEntry->Depex = NULL;\r
+ DriverEntry->Dependent = TRUE;\r
+ DriverEntry->DepexProtocolError = FALSE;\r
+ }\r
+ } else {\r
+ //\r
+ // Set Before, After, and Unrequested state information based on Depex\r
+ // Driver will be put in Dependent or Unrequested state\r
+ //\r
+ SmmPreProcessDepex (DriverEntry);\r
+ DriverEntry->DepexProtocolError = FALSE;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check every driver and locate a matching one. If the driver is found, the Unrequested\r
+ state flag is cleared.\r
+\r
+ @param FirmwareVolumeHandle The handle of the Firmware Volume that contains\r
+ the firmware file specified by DriverName.\r
+ @param DriverName The Driver name to put in the Dependent state.\r
+\r
+ @retval EFI_SUCCESS The DriverName was found and it's SOR bit was\r
+ cleared\r
+ @retval EFI_NOT_FOUND The DriverName does not exist or it's SOR bit was\r
+ not set.\r
+\r
+**/\r
+EFI_STATUS\r
+SmmSchedule (\r
+ IN EFI_HANDLE FirmwareVolumeHandle,\r
+ IN EFI_GUID *DriverName\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ EFI_SMM_DRIVER_ENTRY *DriverEntry;\r
+\r
+ //\r
+ // Check every driver\r
+ //\r
+ for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {\r
+ DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE);\r
+ if (DriverEntry->FvHandle == FirmwareVolumeHandle &&\r
+ DriverEntry->Unrequested &&\r
+ CompareGuid (DriverName, &DriverEntry->FileName)) {\r
+ //\r
+ // Move the driver from the Unrequested to the Dependent state\r
+ //\r
+ DriverEntry->Unrequested = FALSE;\r
+ DriverEntry->Dependent = TRUE;\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ This is the main Dispatcher for SMM and it exits when there are no more\r
+ drivers to run. Drain the mScheduledQueue and load and start a PE\r
+ image for each driver. Search the mDiscoveredList to see if any driver can\r
+ be placed on the mScheduledQueue. If no drivers are placed on the\r
+ mScheduledQueue exit the function. On exit it is assumed the Bds()\r
+ will be called, and when the Bds() exits the Dispatcher will be called\r
+ again.\r
+\r
+ @retval EFI_ALREADY_STARTED The SMM Dispatcher is already running\r
+ @retval EFI_NOT_FOUND No SMM Drivers were dispatched\r
+ @retval EFI_SUCCESS One or more SMM Drivers were dispatched\r
+\r
+**/\r
+EFI_STATUS\r
+SmmDispatcher (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_STATUS ReturnStatus;\r
+ LIST_ENTRY *Link;\r
+ EFI_SMM_DRIVER_ENTRY *DriverEntry;\r
+ BOOLEAN ReadyToRun;\r
+\r
+ if (!gRequestDispatch) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if (gDispatcherRunning) {\r
+ //\r
+ // If the dispatcher is running don't let it be restarted.\r
+ //\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ gDispatcherRunning = TRUE;\r
+\r
+ ReturnStatus = EFI_NOT_FOUND;\r
+ do {\r
+ //\r
+ // Drain the Scheduled Queue\r
+ //\r
+ while (!IsListEmpty (&mScheduledQueue)) {\r
+ DriverEntry = CR (\r
+ mScheduledQueue.ForwardLink,\r
+ EFI_SMM_DRIVER_ENTRY,\r
+ ScheduledLink,\r
+ EFI_SMM_DRIVER_ENTRY_SIGNATURE\r
+ );\r
+\r
+ //\r
+ // Load the SMM Driver image into memory. If the Driver was transitioned from\r
+ // Untrused to Scheduled it would have already been loaded so we may need to\r
+ // skip the LoadImage\r
+ //\r
+ if (DriverEntry->ImageHandle == NULL) {\r
+ Status = SmmLoadImage (DriverEntry);\r
+\r
+ //\r
+ // Update the driver state to reflect that it's been loaded\r
+ //\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ if (Status == EFI_SECURITY_VIOLATION) {\r
+ //\r
+ // Take driver from Scheduled to Untrused state\r
+ //\r
+ DriverEntry->Untrusted = TRUE;\r
+ } else {\r
+ //\r
+ // The SMM Driver could not be loaded, and do not attempt to load or start it again.\r
+ // Take driver from Scheduled to Initialized.\r
+ //\r
+ // This case include the Never Trusted state if EFI_ACCESS_DENIED is returned\r
+ //\r
+ DriverEntry->Initialized = TRUE;\r
+ }\r
+\r
+ DriverEntry->Scheduled = FALSE;\r
+ RemoveEntryList (&DriverEntry->ScheduledLink);\r
+\r
+ //\r
+ // If it's an error don't try the StartImage\r
+ //\r
+ continue;\r
+ }\r
+ }\r
+\r
+ DriverEntry->Scheduled = FALSE;\r
+ DriverEntry->Initialized = TRUE;\r
+ RemoveEntryList (&DriverEntry->ScheduledLink);\r
+\r
+ REPORT_STATUS_CODE_WITH_EXTENDED_DATA (\r
+ EFI_PROGRESS_CODE,\r
+ EFI_SOFTWARE_SMM_DRIVER | EFI_SW_PC_INIT_BEGIN,\r
+ &DriverEntry->ImageHandle,\r
+ sizeof (DriverEntry->ImageHandle)\r
+ );\r
+\r
+ //\r
+ // For each SMM driver, pass NULL as ImageHandle\r
+ //\r
+ Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)DriverEntry->ImageEntryPoint)(DriverEntry->ImageHandle, gST);\r
+ if (EFI_ERROR(Status)){\r
+ SmmFreePages(DriverEntry->ImageBuffer, DriverEntry->NumberOfPage);\r
+ }\r
+\r
+ REPORT_STATUS_CODE_WITH_EXTENDED_DATA (\r
+ EFI_PROGRESS_CODE,\r
+ EFI_SOFTWARE_SMM_DRIVER | EFI_SW_PC_INIT_END,\r
+ &DriverEntry->ImageHandle,\r
+ sizeof (DriverEntry->ImageHandle)\r
+ );\r
+\r
+ ReturnStatus = EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Search DriverList for items to place on Scheduled Queue\r
+ //\r
+ ReadyToRun = FALSE;\r
+ for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {\r
+ DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE);\r
+\r
+ if (DriverEntry->DepexProtocolError){\r
+ //\r
+ // If Section Extraction Protocol did not let the Depex be read before retry the read\r
+ //\r
+ Status = SmmGetDepexSectionAndPreProccess (DriverEntry);\r
+ }\r
+\r
+ if (DriverEntry->Dependent) {\r
+ if (SmmIsSchedulable (DriverEntry)) {\r
+ SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);\r
+ ReadyToRun = TRUE;\r
+ }\r
+ }\r
+ }\r
+ } while (ReadyToRun);\r
+\r
+ //\r
+ // If there is no more SMM driver to dispatch, stop the dispatch request\r
+ //\r
+ gRequestDispatch = FALSE;\r
+ for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {\r
+ DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE);\r
+\r
+ if (!DriverEntry->Initialized){\r
+ //\r
+ // We have SMM driver pending to dispatch\r
+ //\r
+ gRequestDispatch = TRUE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ gDispatcherRunning = FALSE;\r
+\r
+ return ReturnStatus;\r
+}\r
+\r
+/**\r
+ Insert InsertedDriverEntry onto the mScheduledQueue. To do this you\r
+ must add any driver with a before dependency on InsertedDriverEntry first.\r
+ You do this by recursively calling this routine. After all the Befores are\r
+ processed you can add InsertedDriverEntry to the mScheduledQueue.\r
+ Then you can add any driver with an After dependency on InsertedDriverEntry\r
+ by recursively calling this routine.\r
+\r
+ @param InsertedDriverEntry The driver to insert on the ScheduledLink Queue\r
+\r
+**/\r
+VOID\r
+SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (\r
+ IN EFI_SMM_DRIVER_ENTRY *InsertedDriverEntry\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ EFI_SMM_DRIVER_ENTRY *DriverEntry;\r
+\r
+ //\r
+ // Process Before Dependency\r
+ //\r
+ for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {\r
+ DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE);\r
+ if (DriverEntry->Before && DriverEntry->Dependent) {\r
+ if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) {\r
+ //\r
+ // Recursively process BEFORE\r
+ //\r
+ SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Convert driver from Dependent to Scheduled state\r
+ //\r
+\r
+ InsertedDriverEntry->Dependent = FALSE;\r
+ InsertedDriverEntry->Scheduled = TRUE;\r
+ InsertTailList (&mScheduledQueue, &InsertedDriverEntry->ScheduledLink);\r
+\r
+\r
+ //\r
+ // Process After Dependency\r
+ //\r
+ for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {\r
+ DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE);\r
+ if (DriverEntry->After && DriverEntry->Dependent) {\r
+ if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) {\r
+ //\r
+ // Recursively process AFTER\r
+ //\r
+ SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Return TRUE if the Fv has been processed, FALSE if not.\r
+\r
+ @param FvHandle The handle of a FV that's being tested\r
+\r
+ @retval TRUE Fv protocol on FvHandle has been processed\r
+ @retval FALSE Fv protocol on FvHandle has not yet been\r
+ processed\r
+\r
+**/\r
+BOOLEAN\r
+FvHasBeenProcessed (\r
+ IN EFI_HANDLE FvHandle\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ KNOWN_HANDLE *KnownHandle;\r
+\r
+ for (Link = mFvHandleList.ForwardLink; Link != &mFvHandleList; Link = Link->ForwardLink) {\r
+ KnownHandle = CR(Link, KNOWN_HANDLE, Link, KNOWN_HANDLE_SIGNATURE);\r
+ if (KnownHandle->Handle == FvHandle) {\r
+ return TRUE;\r
+ }\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Remember that Fv protocol on FvHandle has had it's drivers placed on the\r
+ mDiscoveredList. This fucntion adds entries on the mFvHandleList. Items are\r
+ never removed/freed from the mFvHandleList.\r
+\r
+ @param FvHandle The handle of a FV that has been processed\r
+\r
+**/\r
+VOID\r
+FvIsBeingProcesssed (\r
+ IN EFI_HANDLE FvHandle\r
+ )\r
+{\r
+ KNOWN_HANDLE *KnownHandle;\r
+\r
+ KnownHandle = AllocatePool (sizeof (KNOWN_HANDLE));\r
+ ASSERT (KnownHandle != NULL);\r
+\r
+ KnownHandle->Signature = KNOWN_HANDLE_SIGNATURE;\r
+ KnownHandle->Handle = FvHandle;\r
+ InsertTailList (&mFvHandleList, &KnownHandle->Link);\r
+}\r
+\r
+/**\r
+ Convert FvHandle and DriverName into an EFI device path\r
+\r
+ @param Fv Fv protocol, needed to read Depex info out of\r
+ FLASH.\r
+ @param FvHandle Handle for Fv, needed in the\r
+ EFI_SMM_DRIVER_ENTRY so that the PE image can be\r
+ read out of the FV at a later time.\r
+ @param DriverName Name of driver to add to mDiscoveredList.\r
+\r
+ @return Pointer to device path constructed from FvHandle and DriverName\r
+\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+SmmFvToDevicePath (\r
+ IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv,\r
+ IN EFI_HANDLE FvHandle,\r
+ IN EFI_GUID *DriverName\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DEVICE_PATH_PROTOCOL *FvDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *FileNameDevicePath;\r
+\r
+ //\r
+ // Remember the device path of the FV\r
+ //\r
+ Status = gBS->HandleProtocol (FvHandle, &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath);\r
+ if (EFI_ERROR (Status)) {\r
+ FileNameDevicePath = NULL;\r
+ } else {\r
+ //\r
+ // Build a device path to the file in the FV to pass into gBS->LoadImage\r
+ //\r
+ EfiInitializeFwVolDevicepathNode (&mFvDevicePath.File, DriverName);\r
+ SetDevicePathEndNode (&mFvDevicePath.End);\r
+\r
+ //\r
+ // Note: FileNameDevicePath is in DXE memory\r
+ //\r
+ FileNameDevicePath = AppendDevicePath (\r
+ FvDevicePath,\r
+ (EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath\r
+ );\r
+ }\r
+ return FileNameDevicePath;\r
+}\r
+\r
+/**\r
+ Add an entry to the mDiscoveredList. Allocate memory to store the DriverEntry,\r
+ and initilize any state variables. Read the Depex from the FV and store it\r
+ in DriverEntry. Pre-process the Depex to set the SOR, Before and After state.\r
+ The Discovered list is never free'ed and contains booleans that represent the\r
+ other possible SMM driver states.\r
+\r
+ @param Fv Fv protocol, needed to read Depex info out of\r
+ FLASH.\r
+ @param FvHandle Handle for Fv, needed in the\r
+ EFI_SMM_DRIVER_ENTRY so that the PE image can be\r
+ read out of the FV at a later time.\r
+ @param DriverName Name of driver to add to mDiscoveredList.\r
+\r
+ @retval EFI_SUCCESS If driver was added to the mDiscoveredList.\r
+ @retval EFI_ALREADY_STARTED The driver has already been started. Only one\r
+ DriverName may be active in the system at any one\r
+ time.\r
+\r
+**/\r
+EFI_STATUS\r
+SmmAddToDriverList (\r
+ IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv,\r
+ IN EFI_HANDLE FvHandle,\r
+ IN EFI_GUID *DriverName\r
+ )\r
+{\r
+ EFI_SMM_DRIVER_ENTRY *DriverEntry;\r
+\r
+ //\r
+ // Create the Driver Entry for the list. ZeroPool initializes lots of variables to\r
+ // NULL or FALSE.\r
+ //\r
+ DriverEntry = AllocateZeroPool (sizeof (EFI_SMM_DRIVER_ENTRY));\r
+ ASSERT (DriverEntry != NULL);\r
+\r
+ DriverEntry->Signature = EFI_SMM_DRIVER_ENTRY_SIGNATURE;\r
+ CopyGuid (&DriverEntry->FileName, DriverName);\r
+ DriverEntry->FvHandle = FvHandle;\r
+ DriverEntry->Fv = Fv;\r
+ DriverEntry->FvFileDevicePath = SmmFvToDevicePath (Fv, FvHandle, DriverName);\r
+\r
+ SmmGetDepexSectionAndPreProccess (DriverEntry);\r
+\r
+ InsertTailList (&mDiscoveredList, &DriverEntry->Link);\r
+ gRequestDispatch = TRUE;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This function is the main entry point for an SMM handler dispatch\r
+ or communicate-based callback.\r
+\r
+ Event notification that is fired every time a FV dispatch protocol is added.\r
+ More than one protocol may have been added when this event is fired, so you\r
+ must loop on SmmLocateHandle () to see how many protocols were added and\r
+ do the following to each FV:\r
+ If the Fv has already been processed, skip it. If the Fv has not been\r
+ processed then mark it as being processed, as we are about to process it.\r
+ Read the Fv and add any driver in the Fv to the mDiscoveredList.The\r
+ mDiscoveredList is never free'ed and contains variables that define\r
+ the other states the SMM driver transitions to..\r
+ While you are at it read the A Priori file into memory.\r
+ Place drivers in the A Priori list onto the mScheduledQueue.\r
+\r
+ @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().\r
+ @param Context Points to an optional handler context which was specified when the handler was registered.\r
+ @param CommBuffer A pointer to a collection of data in memory that will\r
+ be conveyed from a non-SMM environment into an SMM environment.\r
+ @param CommBufferSize The size of the CommBuffer.\r
+\r
+ @return Status Code\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmDriverDispatchHandler (\r
+ IN EFI_HANDLE DispatchHandle,\r
+ IN CONST VOID *Context, OPTIONAL\r
+ IN OUT VOID *CommBuffer, OPTIONAL\r
+ IN OUT UINTN *CommBufferSize OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN HandleCount;\r
+ EFI_HANDLE *HandleBuffer;\r
+ EFI_STATUS GetNextFileStatus;\r
+ EFI_STATUS SecurityStatus;\r
+ EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;\r
+ EFI_DEVICE_PATH_PROTOCOL *FvDevicePath;\r
+ EFI_HANDLE FvHandle;\r
+ EFI_GUID NameGuid;\r
+ UINTN Key;\r
+ EFI_FV_FILETYPE Type;\r
+ EFI_FV_FILE_ATTRIBUTES Attributes;\r
+ UINTN Size;\r
+ EFI_SMM_DRIVER_ENTRY *DriverEntry;\r
+ EFI_GUID *AprioriFile;\r
+ UINTN AprioriEntryCount;\r
+ UINTN Index;\r
+ LIST_ENTRY *Link;\r
+ UINT32 AuthenticationStatus;\r
+ UINTN SizeOfBuffer;\r
+\r
+ HandleBuffer = NULL;\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiFirmwareVolume2ProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &HandleBuffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ FvHandle = HandleBuffer[Index];\r
+\r
+ if (FvHasBeenProcessed (FvHandle)) {\r
+ //\r
+ // This Fv has already been processed so lets skip it!\r
+ //\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Since we are about to process this Fv mark it as processed.\r
+ //\r
+ FvIsBeingProcesssed (FvHandle);\r
+\r
+ Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // FvHandle must have a Firmware Volume2 Protocol thus we should never get here.\r
+ //\r
+ ASSERT (FALSE);\r
+ continue;\r
+ }\r
+\r
+ Status = gBS->HandleProtocol (FvHandle, &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // The Firmware volume doesn't have device path, can't be dispatched.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // If the Security Architectural Protocol has not been located yet, then attempt to locate it\r
+ //\r
+ if (mSecurity == NULL) {\r
+ gBS->LocateProtocol (&gEfiSecurityArchProtocolGuid, NULL, (VOID**)&mSecurity);\r
+ }\r
+\r
+ //\r
+ // Evaluate the authentication status of the Firmware Volume through\r
+ // Security Architectural Protocol\r
+ //\r
+ if (mSecurity != NULL) {\r
+ SecurityStatus = mSecurity->FileAuthenticationState (\r
+ mSecurity,\r
+ 0,\r
+ FvDevicePath\r
+ );\r
+ if (SecurityStatus != EFI_SUCCESS) {\r
+ //\r
+ // Security check failed. The firmware volume should not be used for any purpose.\r
+ //\r
+ continue;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Discover Drivers in FV and add them to the Discovered Driver List.\r
+ // Process EFI_FV_FILETYPE_SMM type and then EFI_FV_FILETYPE_COMBINED_SMM_DXE\r
+ //\r
+ for (Index = 0; Index < sizeof (mSmmFileTypes)/sizeof (EFI_FV_FILETYPE); Index++) {\r
+ //\r
+ // Initialize the search key\r
+ //\r
+ Key = 0;\r
+ do {\r
+ Type = mSmmFileTypes[Index];\r
+ GetNextFileStatus = Fv->GetNextFile (\r
+ Fv,\r
+ &Key,\r
+ &Type,\r
+ &NameGuid,\r
+ &Attributes,\r
+ &Size\r
+ );\r
+ if (!EFI_ERROR (GetNextFileStatus)) {\r
+ SmmAddToDriverList (Fv, FvHandle, &NameGuid);\r
+ }\r
+ } while (!EFI_ERROR (GetNextFileStatus));\r
+ }\r
+\r
+ //\r
+ // Read the array of GUIDs from the Apriori file if it is present in the firmware volume\r
+ // (Note: AprioriFile is in DXE memory)\r
+ //\r
+ AprioriFile = NULL;\r
+ Status = Fv->ReadSection (\r
+ Fv,\r
+ &gAprioriGuid,\r
+ EFI_SECTION_RAW,\r
+ 0,\r
+ (VOID **)&AprioriFile,\r
+ &SizeOfBuffer,\r
+ &AuthenticationStatus\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ AprioriEntryCount = SizeOfBuffer / sizeof (EFI_GUID);\r
+ } else {\r
+ AprioriEntryCount = 0;\r
+ }\r
+\r
+ //\r
+ // Put drivers on Apriori List on the Scheduled queue. The Discovered List includes\r
+ // drivers not in the current FV and these must be skipped since the a priori list\r
+ // is only valid for the FV that it resided in.\r
+ //\r
+\r
+ for (Index = 0; Index < AprioriEntryCount; Index++) {\r
+ for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {\r
+ DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE);\r
+ if (CompareGuid (&DriverEntry->FileName, &AprioriFile[Index]) &&\r
+ (FvHandle == DriverEntry->FvHandle)) {\r
+ DriverEntry->Dependent = FALSE;\r
+ DriverEntry->Scheduled = TRUE;\r
+ InsertTailList (&mScheduledQueue, &DriverEntry->ScheduledLink);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Free data allocated by Fv->ReadSection ()\r
+ //\r
+ // The UEFI Boot Services FreePool() function must be used because Fv->ReadSection \r
+ // used the UEFI Boot Services AllocatePool() function\r
+ //\r
+ gBS->FreePool (AprioriFile);\r
+ }\r
+\r
+ //\r
+ // Execute the SMM Dispatcher on any newly discovered FVs and previously \r
+ // discovered SMM drivers that have been discovered but not dispatched.\r
+ //\r
+ return SmmDispatcher ();\r
+}\r
+\r
+/**\r
+ Traverse the discovered list for any drivers that were discovered but not loaded\r
+ because the dependency experessions evaluated to false.\r
+\r
+**/\r
+VOID\r
+SmmDisplayDiscoveredNotDispatched (\r
+ VOID\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ EFI_SMM_DRIVER_ENTRY *DriverEntry;\r
+\r
+ for (Link = mDiscoveredList.ForwardLink;Link !=&mDiscoveredList; Link = Link->ForwardLink) {\r
+ DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE);\r
+ if (DriverEntry->Dependent) {\r
+ DEBUG ((DEBUG_LOAD, "SMM Driver %g was discovered but not loaded!!\n", &DriverEntry->FileName));\r
+ }\r
+ }\r
+}\r