]> git.proxmox.com Git - mirror_edk2.git/blobdiff - StandaloneMmPkg/Core/Dispatcher.c
StandaloneMmPkg/Core: Implementation of Standalone MM Core Module.
[mirror_edk2.git] / StandaloneMmPkg / Core / Dispatcher.c
diff --git a/StandaloneMmPkg/Core/Dispatcher.c b/StandaloneMmPkg/Core/Dispatcher.c
new file mode 100644 (file)
index 0000000..8d009b4
--- /dev/null
@@ -0,0 +1,1071 @@
+/** @file\r
+  MM 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 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
+\r
+  Copyright (c) 2014, Hewlett-Packard Development Company, L.P.\r
+  Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2016 - 2018, ARM Limited. All rights reserved.<BR>\r
+\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 "StandaloneMmCore.h"\r
+\r
+//\r
+// MM Dispatcher Data structures\r
+//\r
+#define KNOWN_HANDLE_SIGNATURE  SIGNATURE_32('k','n','o','w')\r
+\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
+EFI_STATUS\r
+MmCoreFfsFindMmDriver (\r
+  IN  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader\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
+MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (\r
+  IN  EFI_MM_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_MM_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_MM_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 MM Dispacher.  TRUE if dispatcher is execuing.\r
+//\r
+BOOLEAN  gDispatcherRunning = FALSE;\r
+\r
+//\r
+// Flag for the MM Dispacher.  TRUE if there is one or more MM drivers ready to be dispatched\r
+//\r
+BOOLEAN  gRequestDispatch = FALSE;\r
+\r
+//\r
+// The global variable is defined for Loading modules at fixed address feature to track the MM code\r
+// memory range usage. It is a bit mapped array in which every bit indicates the correspoding\r
+// memory page available or not.\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED    UINT64                *mMmCodeMemoryRangeUsageBitMap=NULL;\r
+\r
+/**\r
+  To check memory usage bit map array to figure out if the memory range in which the image will be loaded\r
+  is available or not. If memory range is avaliable, the function will mark the correponding bits to 1\r
+  which indicates the memory range is used. The function is only invoked when load modules at fixed address\r
+  feature is enabled.\r
+\r
+  @param  ImageBase                The base addres the image will be loaded at.\r
+  @param  ImageSize                The size of the image\r
+\r
+  @retval EFI_SUCCESS              The memory range the image will be loaded in is available\r
+  @retval EFI_NOT_FOUND            The memory range the image will be loaded in is not available\r
+**/\r
+EFI_STATUS\r
+CheckAndMarkFixLoadingMemoryUsageBitMap (\r
+  IN  EFI_PHYSICAL_ADDRESS          ImageBase,\r
+  IN  UINTN                         ImageSize\r
+  )\r
+{\r
+  UINT32                             MmCodePageNumber;\r
+  UINT64                             MmCodeSize;\r
+  EFI_PHYSICAL_ADDRESS               MmCodeBase;\r
+  UINTN                              BaseOffsetPageNumber;\r
+  UINTN                              TopOffsetPageNumber;\r
+  UINTN                              Index;\r
+\r
+  //\r
+  // Build tool will calculate the smm code size and then patch the PcdLoadFixAddressMmCodePageNumber\r
+  //\r
+  MmCodePageNumber = 0;\r
+  MmCodeSize = EFI_PAGES_TO_SIZE (MmCodePageNumber);\r
+  MmCodeBase = gLoadModuleAtFixAddressMmramBase;\r
+\r
+  //\r
+  // If the memory usage bit map is not initialized,  do it. Every bit in the array\r
+  // indicate the status of the corresponding memory page, available or not\r
+  //\r
+  if (mMmCodeMemoryRangeUsageBitMap == NULL) {\r
+    mMmCodeMemoryRangeUsageBitMap = AllocateZeroPool (((MmCodePageNumber / 64) + 1) * sizeof (UINT64));\r
+  }\r
+\r
+  //\r
+  // If the Dxe code memory range is not allocated or the bit map array allocation failed, return EFI_NOT_FOUND\r
+  //\r
+  if (mMmCodeMemoryRangeUsageBitMap == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // see if the memory range for loading the image is in the MM code range.\r
+  //\r
+  if (MmCodeBase + MmCodeSize <  ImageBase + ImageSize || MmCodeBase >  ImageBase) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // Test if the memory is avalaible or not.\r
+  //\r
+  BaseOffsetPageNumber = (UINTN)EFI_SIZE_TO_PAGES ((UINT32)(ImageBase - MmCodeBase));\r
+  TopOffsetPageNumber  = (UINTN)EFI_SIZE_TO_PAGES ((UINT32)(ImageBase + ImageSize - MmCodeBase));\r
+  for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index ++) {\r
+    if ((mMmCodeMemoryRangeUsageBitMap[Index / 64] & LShiftU64 (1, (Index % 64))) != 0) {\r
+      //\r
+      // This page is already used.\r
+      //\r
+      return EFI_NOT_FOUND;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Being here means the memory range is available.  So mark the bits for the memory range\r
+  //\r
+  for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index ++) {\r
+    mMmCodeMemoryRangeUsageBitMap[Index / 64] |= LShiftU64 (1, (Index % 64));\r
+  }\r
+  return  EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Get the fixed loading address from image header assigned by build tool. This function only be called\r
+  when Loading module at Fixed address feature enabled.\r
+\r
+  @param  ImageContext              Pointer to the image context structure that describes the PE/COFF\r
+                                    image that needs to be examined by this function.\r
+  @retval EFI_SUCCESS               An fixed loading address is assigned to this image by build tools .\r
+  @retval EFI_NOT_FOUND             The image has no assigned fixed loadding address.\r
+\r
+**/\r
+EFI_STATUS\r
+GetPeCoffImageFixLoadingAssignedAddress(\r
+  IN OUT PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext\r
+  )\r
+{\r
+  UINTN                              SectionHeaderOffset;\r
+  EFI_STATUS                         Status;\r
+  EFI_IMAGE_SECTION_HEADER           SectionHeader;\r
+  EFI_IMAGE_OPTIONAL_HEADER_UNION    *ImgHdr;\r
+  EFI_PHYSICAL_ADDRESS               FixLoadingAddress;\r
+  UINT16                             Index;\r
+  UINTN                              Size;\r
+  UINT16                             NumberOfSections;\r
+  UINT64                             ValueInSectionHeader;\r
+\r
+  FixLoadingAddress = 0;\r
+  Status = EFI_NOT_FOUND;\r
+\r
+  //\r
+  // Get PeHeader pointer\r
+  //\r
+  ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((CHAR8* )ImageContext->Handle + ImageContext->PeCoffHeaderOffset);\r
+  SectionHeaderOffset = ImageContext->PeCoffHeaderOffset + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) +\r
+    ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader;\r
+  NumberOfSections = ImgHdr->Pe32.FileHeader.NumberOfSections;\r
+\r
+  //\r
+  // Get base address from the first section header that doesn't point to code section.\r
+  //\r
+  for (Index = 0; Index < NumberOfSections; Index++) {\r
+    //\r
+    // Read section header from file\r
+    //\r
+    Size = sizeof (EFI_IMAGE_SECTION_HEADER);\r
+    Status = ImageContext->ImageRead (\r
+                             ImageContext->Handle,\r
+                             SectionHeaderOffset,\r
+                             &Size,\r
+                             &SectionHeader\r
+                             );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    Status = EFI_NOT_FOUND;\r
+\r
+    if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_CNT_CODE) == 0) {\r
+      //\r
+      // Build tool will save the address in PointerToRelocations & PointerToLineNumbers fields\r
+      // in the first section header that doesn't point to code section in image header. So there\r
+      // is an assumption that when the feature is enabled, if a module with a loading address\r
+      // assigned by tools, the PointerToRelocations & PointerToLineNumbers fields should not be\r
+      // Zero, or else, these 2 fields should be set to Zero\r
+      //\r
+      ValueInSectionHeader = ReadUnaligned64 ((UINT64*)&SectionHeader.PointerToRelocations);\r
+      if (ValueInSectionHeader != 0) {\r
+        //\r
+        // Found first section header that doesn't point to code section in which build tool saves the\r
+        // offset to SMRAM base as image base in PointerToRelocations & PointerToLineNumbers fields\r
+        //\r
+        FixLoadingAddress = (EFI_PHYSICAL_ADDRESS)(gLoadModuleAtFixAddressMmramBase + (INT64)ValueInSectionHeader);\r
+        //\r
+        // Check if the memory range is available.\r
+        //\r
+        Status = CheckAndMarkFixLoadingMemoryUsageBitMap (FixLoadingAddress, (UINTN)(ImageContext->ImageSize + ImageContext->SectionAlignment));\r
+        if (!EFI_ERROR(Status)) {\r
+          //\r
+          // The assigned address is valid. Return the specified loading address\r
+          //\r
+          ImageContext->ImageAddress = FixLoadingAddress;\r
+        }\r
+      }\r
+      break;\r
+    }\r
+    SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);\r
+  }\r
+  DEBUG ((DEBUG_INFO|DEBUG_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address %x, Status = %r\n",\r
+          FixLoadingAddress, Status));\r
+  return Status;\r
+}\r
+/**\r
+  Loads an EFI image into SMRAM.\r
+\r
+  @param  DriverEntry             EFI_MM_DRIVER_ENTRY instance\r
+\r
+  @return EFI_STATUS\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+MmLoadImage (\r
+  IN OUT EFI_MM_DRIVER_ENTRY  *DriverEntry\r
+  )\r
+{\r
+  VOID                           *Buffer;\r
+  UINTN                          PageCount;\r
+  EFI_STATUS                     Status;\r
+  EFI_PHYSICAL_ADDRESS           DstBuffer;\r
+  PE_COFF_LOADER_IMAGE_CONTEXT   ImageContext;\r
+\r
+  DEBUG ((DEBUG_INFO, "MmLoadImage - %g\n", &DriverEntry->FileName));\r
+\r
+  Buffer = AllocateCopyPool (DriverEntry->Pe32DataSize, DriverEntry->Pe32Data);\r
+  if (Buffer == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status               = EFI_SUCCESS;\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
+      MmFreePool (Buffer);\r
+    }\r
+    return Status;\r
+  }\r
+\r
+  PageCount = (UINTN)EFI_SIZE_TO_PAGES ((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment);\r
+  DstBuffer = (UINTN)(-1);\r
+\r
+  Status = MmAllocatePages (\r
+             AllocateMaxAddress,\r
+             EfiRuntimeServicesCode,\r
+             PageCount,\r
+             &DstBuffer\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    if (Buffer != NULL) {\r
+      MmFreePool (Buffer);\r
+    }\r
+    return Status;\r
+  }\r
+\r
+  ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer;\r
+\r
+  //\r
+  // Align buffer on section boundry\r
+  //\r
+  ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;\r
+  ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(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
+      MmFreePool (Buffer);\r
+    }\r
+    MmFreePages (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
+      MmFreePool (Buffer);\r
+    }\r
+    MmFreePages (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
+  if (mEfiSystemTable != NULL) {\r
+    Status = mEfiSystemTable->BootServices->AllocatePool (\r
+                                              EfiBootServicesData,\r
+                                              sizeof (EFI_LOADED_IMAGE_PROTOCOL),\r
+                                              (VOID **)&DriverEntry->LoadedImage\r
+                                              );\r
+    if (EFI_ERROR (Status)) {\r
+      if (Buffer != NULL) {\r
+        MmFreePool (Buffer);\r
+      }\r
+      MmFreePages (DstBuffer, PageCount);\r
+      return Status;\r
+    }\r
+\r
+    ZeroMem (DriverEntry->LoadedImage, sizeof (EFI_LOADED_IMAGE_PROTOCOL));\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  = NULL;\r
+    DriverEntry->LoadedImage->SystemTable   = mEfiSystemTable;\r
+    DriverEntry->LoadedImage->DeviceHandle  = NULL;\r
+    DriverEntry->LoadedImage->FilePath      = NULL;\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 MM Driver\r
+    //\r
+    DriverEntry->ImageHandle = NULL;\r
+    Status = mEfiSystemTable->BootServices->InstallMultipleProtocolInterfaces (\r
+                                              &DriverEntry->ImageHandle,\r
+                                              &gEfiLoadedImageProtocolGuid,\r
+                                              DriverEntry->LoadedImage,\r
+                                              NULL\r
+                                              );\r
+  }\r
+\r
+  //\r
+  // Print the load address and the PDB file name if it is available\r
+  //\r
+  DEBUG_CODE_BEGIN ();\r
+\r
+  UINTN Index;\r
+  UINTN StartIndex;\r
+  CHAR8 EfiFileName[256];\r
+\r
+  DEBUG ((DEBUG_INFO | DEBUG_LOAD,\r
+          "Loading MM driver at 0x%11p EntryPoint=0x%11p ",\r
+          (VOID *)(UINTN) ImageContext.ImageAddress,\r
+          FUNCTION_ENTRY_POINT (ImageContext.EntryPoint)));\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
+    //\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));\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
+  MmFreePool (Buffer);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Preprocess dependency expression and update DriverEntry to reflect the\r
+  state of  Before and After dependencies. If DriverEntry->Before\r
+  or DriverEntry->After is set it will never be cleared.\r
+\r
+  @param  DriverEntry           DriverEntry element to update .\r
+\r
+  @retval EFI_SUCCESS           It always works.\r
+\r
+**/\r
+EFI_STATUS\r
+MmPreProcessDepex (\r
+  IN EFI_MM_DRIVER_ENTRY  *DriverEntry\r
+  )\r
+{\r
+  UINT8  *Iterator;\r
+\r
+  Iterator = DriverEntry->Depex;\r
+  DriverEntry->Dependent = TRUE;\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
+MmGetDepexSectionAndPreProccess (\r
+  IN EFI_MM_DRIVER_ENTRY  *DriverEntry\r
+  )\r
+{\r
+  EFI_STATUS                     Status;\r
+\r
+  //\r
+  // Data already read\r
+  //\r
+  if (DriverEntry->Depex == NULL) {\r
+    Status = EFI_NOT_FOUND;\r
+  } else {\r
+    Status = EFI_SUCCESS;\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 and After state information based on Depex\r
+    // Driver will be put in Dependent state\r
+    //\r
+    MmPreProcessDepex (DriverEntry);\r
+    DriverEntry->DepexProtocolError = FALSE;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This is the main Dispatcher for MM 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.\r
+\r
+  @retval EFI_SUCCESS           All of the MM Drivers that could be dispatched\r
+                                have been run and the MM Entry Point has been\r
+                                registered.\r
+  @retval EFI_NOT_READY         The MM Driver that registered the MM Entry Point\r
+                                was just dispatched.\r
+  @retval EFI_NOT_FOUND         There are no MM Drivers available to be dispatched.\r
+  @retval EFI_ALREADY_STARTED   The MM Dispatcher is already running\r
+\r
+**/\r
+EFI_STATUS\r
+MmDispatcher (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  LIST_ENTRY            *Link;\r
+  EFI_MM_DRIVER_ENTRY  *DriverEntry;\r
+  BOOLEAN               ReadyToRun;\r
+  BOOLEAN               PreviousMmEntryPointRegistered;\r
+\r
+  DEBUG ((DEBUG_INFO, "MmDispatcher\n"));\r
+\r
+  if (!gRequestDispatch) {\r
+    DEBUG ((DEBUG_INFO, "  !gRequestDispatch\n"));\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  if (gDispatcherRunning) {\r
+    DEBUG ((DEBUG_INFO, "  gDispatcherRunning\n"));\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
+  do {\r
+    //\r
+    // Drain the Scheduled Queue\r
+    //\r
+    DEBUG ((DEBUG_INFO, "  Drain the Scheduled Queue\n"));\r
+    while (!IsListEmpty (&mScheduledQueue)) {\r
+      DriverEntry = CR (\r
+                      mScheduledQueue.ForwardLink,\r
+                      EFI_MM_DRIVER_ENTRY,\r
+                      ScheduledLink,\r
+                      EFI_MM_DRIVER_ENTRY_SIGNATURE\r
+                      );\r
+      DEBUG ((DEBUG_INFO, "  DriverEntry (Scheduled) - %g\n", &DriverEntry->FileName));\r
+\r
+      //\r
+      // Load the MM 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 = MmLoadImage (DriverEntry);\r
+\r
+        //\r
+        // Update the driver state to reflect that it's been loaded\r
+        //\r
+        if (EFI_ERROR (Status)) {\r
+          //\r
+          // The MM Driver could not be loaded, and do not attempt to load or start it again.\r
+          // Take driver from Scheduled to Initialized.\r
+          //\r
+          DriverEntry->Initialized  = TRUE;\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
+      //\r
+      // Cache state of MmEntryPointRegistered before calling entry point\r
+      //\r
+      PreviousMmEntryPointRegistered = gMmCorePrivate->MmEntryPointRegistered;\r
+\r
+      //\r
+      // For each MM driver, pass NULL as ImageHandle\r
+      //\r
+      if (mEfiSystemTable == NULL) {\r
+        DEBUG ((DEBUG_INFO, "StartImage - 0x%x (Standalone Mode)\n", DriverEntry->ImageEntryPoint));\r
+        Status = ((MM_IMAGE_ENTRY_POINT)(UINTN)DriverEntry->ImageEntryPoint) (DriverEntry->ImageHandle, &gMmCoreMmst);\r
+      } else {\r
+        DEBUG ((DEBUG_INFO, "StartImage - 0x%x (Tradition Mode)\n", DriverEntry->ImageEntryPoint));\r
+        Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)DriverEntry->ImageEntryPoint) (\r
+                                                               DriverEntry->ImageHandle,\r
+                                                               mEfiSystemTable\r
+                                                               );\r
+      }\r
+      if (EFI_ERROR(Status)) {\r
+        DEBUG ((DEBUG_INFO, "StartImage Status - %r\n", Status));\r
+        MmFreePages(DriverEntry->ImageBuffer, DriverEntry->NumberOfPage);\r
+      }\r
+\r
+      if (!PreviousMmEntryPointRegistered && gMmCorePrivate->MmEntryPointRegistered) {\r
+        //\r
+        // Return immediately if the MM Entry Point was registered by the MM\r
+        // Driver that was just dispatched.  The MM IPL will reinvoke the MM\r
+        // Core Dispatcher.  This is required so MM Mode may be enabled as soon\r
+        // as all the dependent MM Drivers for MM Mode have been dispatched.\r
+        // Once the MM Entry Point has been registered, then MM Mode will be\r
+        // used.\r
+        //\r
+        gRequestDispatch = TRUE;\r
+        gDispatcherRunning = FALSE;\r
+        return EFI_NOT_READY;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Search DriverList for items to place on Scheduled Queue\r
+    //\r
+    DEBUG ((DEBUG_INFO, "  Search DriverList for items to place on Scheduled Queue\n"));\r
+    ReadyToRun = FALSE;\r
+    for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {\r
+      DriverEntry = CR (Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);\r
+      DEBUG ((DEBUG_INFO, "  DriverEntry (Discovered) - %g\n", &DriverEntry->FileName));\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 = MmGetDepexSectionAndPreProccess (DriverEntry);\r
+      }\r
+\r
+      if (DriverEntry->Dependent) {\r
+        if (MmIsSchedulable (DriverEntry)) {\r
+          MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);\r
+          ReadyToRun = TRUE;\r
+        }\r
+      }\r
+    }\r
+  } while (ReadyToRun);\r
+\r
+  //\r
+  // If there is no more MM driver to dispatch, stop the dispatch request\r
+  //\r
+  DEBUG ((DEBUG_INFO, "  no more MM driver to dispatch, stop the dispatch request\n"));\r
+  gRequestDispatch = FALSE;\r
+  for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {\r
+    DriverEntry = CR (Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);\r
+    DEBUG ((DEBUG_INFO, "  DriverEntry (Discovered) - %g\n", &DriverEntry->FileName));\r
+\r
+    if (!DriverEntry->Initialized) {\r
+      //\r
+      // We have MM driver pending to dispatch\r
+      //\r
+      gRequestDispatch = TRUE;\r
+      break;\r
+    }\r
+  }\r
+\r
+  gDispatcherRunning = FALSE;\r
+\r
+  return EFI_SUCCESS;\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
+MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (\r
+  IN  EFI_MM_DRIVER_ENTRY   *InsertedDriverEntry\r
+  )\r
+{\r
+  LIST_ENTRY            *Link;\r
+  EFI_MM_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_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);\r
+    if (DriverEntry->Before && DriverEntry->Dependent && DriverEntry != InsertedDriverEntry) {\r
+      DEBUG ((DEBUG_DISPATCH, "Evaluate MM DEPEX for FFS(%g)\n", &DriverEntry->FileName));\r
+      DEBUG ((DEBUG_DISPATCH, "  BEFORE FFS(%g) = ", &DriverEntry->BeforeAfterGuid));\r
+      if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) {\r
+        //\r
+        // Recursively process BEFORE\r
+        //\r
+        DEBUG ((DEBUG_DISPATCH, "TRUE\n  END\n  RESULT = TRUE\n"));\r
+        MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);\r
+      } else {\r
+        DEBUG ((DEBUG_DISPATCH, "FALSE\n  END\n  RESULT = FALSE\n"));\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_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);\r
+    if (DriverEntry->After && DriverEntry->Dependent && DriverEntry != InsertedDriverEntry) {\r
+      DEBUG ((DEBUG_DISPATCH, "Evaluate MM DEPEX for FFS(%g)\n", &DriverEntry->FileName));\r
+      DEBUG ((DEBUG_DISPATCH, "  AFTER FFS(%g) = ", &DriverEntry->BeforeAfterGuid));\r
+      if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) {\r
+        //\r
+        // Recursively process AFTER\r
+        //\r
+        DEBUG ((DEBUG_DISPATCH, "TRUE\n  END\n  RESULT = TRUE\n"));\r
+        MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);\r
+      } else {\r
+        DEBUG ((DEBUG_DISPATCH, "FALSE\n  END\n  RESULT = FALSE\n"));\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
+  DEBUG ((DEBUG_INFO, "FvIsBeingProcesssed - 0x%08x\n", FvHandle));\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
+  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 Before and After state.\r
+  The Discovered list is never free'ed and contains booleans that represent the\r
+  other possible MM 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_MM_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
+MmAddToDriverList (\r
+  IN EFI_HANDLE   FvHandle,\r
+  IN VOID         *Pe32Data,\r
+  IN UINTN        Pe32DataSize,\r
+  IN VOID         *Depex,\r
+  IN UINTN        DepexSize,\r
+  IN EFI_GUID     *DriverName\r
+  )\r
+{\r
+  EFI_MM_DRIVER_ENTRY  *DriverEntry;\r
+\r
+  DEBUG ((DEBUG_INFO, "MmAddToDriverList - %g (0x%08x)\n", DriverName, Pe32Data));\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_MM_DRIVER_ENTRY));\r
+  ASSERT (DriverEntry != NULL);\r
+\r
+  DriverEntry->Signature        = EFI_MM_DRIVER_ENTRY_SIGNATURE;\r
+  CopyGuid (&DriverEntry->FileName, DriverName);\r
+  DriverEntry->FvHandle         = FvHandle;\r
+  DriverEntry->Pe32Data         = Pe32Data;\r
+  DriverEntry->Pe32DataSize     = Pe32DataSize;\r
+  DriverEntry->Depex            = Depex;\r
+  DriverEntry->DepexSize        = DepexSize;\r
+\r
+  MmGetDepexSectionAndPreProccess (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 MM 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 MmLocateHandle () 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 MM 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-MM environment into an MM environment.\r
+  @param  CommBufferSize  The size of the CommBuffer.\r
+\r
+  @return Status Code\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+MmDriverDispatchHandler (\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
+\r
+  DEBUG ((DEBUG_INFO, "MmDriverDispatchHandler\n"));\r
+\r
+  //\r
+  // Execute the MM Dispatcher on any newly discovered FVs and previously\r
+  // discovered MM drivers that have been discovered but not dispatched.\r
+  //\r
+  Status = MmDispatcher ();\r
+\r
+  //\r
+  // Check to see if CommBuffer and CommBufferSize are valid\r
+  //\r
+  if (CommBuffer != NULL && CommBufferSize != NULL) {\r
+    if (*CommBufferSize > 0) {\r
+      if (Status == EFI_NOT_READY) {\r
+        //\r
+        // If a the MM Core Entry Point was just registered, then set flag to\r
+        // request the MM Dispatcher to be restarted.\r
+        //\r
+        *(UINT8 *)CommBuffer = COMM_BUFFER_MM_DISPATCH_RESTART;\r
+      } else if (!EFI_ERROR (Status)) {\r
+        //\r
+        // Set the flag to show that the MM Dispatcher executed without errors\r
+        //\r
+        *(UINT8 *)CommBuffer = COMM_BUFFER_MM_DISPATCH_SUCCESS;\r
+      } else {\r
+        //\r
+        // Set the flag to show that the MM Dispatcher encountered an error\r
+        //\r
+        *(UINT8 *)CommBuffer = COMM_BUFFER_MM_DISPATCH_ERROR;\r
+      }\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function is the main entry point for an MM handler dispatch\r
+  or communicate-based callback.\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-MM environment into an MM environment.\r
+  @param  CommBufferSize  The size of the CommBuffer.\r
+\r
+  @return Status Code\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+MmFvDispatchHandler (\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
+  EFI_MM_COMMUNICATE_FV_DISPATCH_DATA  *CommunicationFvDispatchData;\r
+  EFI_FIRMWARE_VOLUME_HEADER            *FwVolHeader;\r
+\r
+  DEBUG ((DEBUG_INFO, "MmFvDispatchHandler\n"));\r
+\r
+  CommunicationFvDispatchData = CommBuffer;\r
+\r
+  DEBUG ((DEBUG_INFO, "  Dispatch - 0x%016lx - 0x%016lx\n", CommunicationFvDispatchData->Address,\r
+          CommunicationFvDispatchData->Size));\r
+\r
+  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)CommunicationFvDispatchData->Address;\r
+\r
+  MmCoreFfsFindMmDriver (FwVolHeader);\r
+\r
+  //\r
+  // Execute the MM Dispatcher on any newly discovered FVs and previously\r
+  // discovered MM drivers that have been discovered but not dispatched.\r
+  //\r
+  Status = MmDispatcher ();\r
+\r
+  return Status;\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
+MmDisplayDiscoveredNotDispatched (\r
+  VOID\r
+  )\r
+{\r
+  LIST_ENTRY                   *Link;\r
+  EFI_MM_DRIVER_ENTRY         *DriverEntry;\r
+\r
+  for (Link = mDiscoveredList.ForwardLink;Link !=&mDiscoveredList; Link = Link->ForwardLink) {\r
+    DriverEntry = CR (Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);\r
+    if (DriverEntry->Dependent) {\r
+      DEBUG ((DEBUG_LOAD, "MM Driver %g was discovered but not loaded!!\n", &DriverEntry->FileName));\r
+    }\r
+  }\r
+}\r