Home | History | Annotate | Download | only in PiSmmCore
      1 /** @file
      2   SMM Driver Dispatcher.
      3 
      4   Step #1 - When a FV protocol is added to the system every driver in the FV
      5             is added to the mDiscoveredList. The Before, and After Depex are
      6             pre-processed as drivers are added to the mDiscoveredList. If an Apriori
      7             file exists in the FV those drivers are addeded to the
      8             mScheduledQueue. The mFvHandleList is used to make sure a
      9             FV is only processed once.
     10 
     11   Step #2 - Dispatch. Remove driver from the mScheduledQueue and load and
     12             start it. After mScheduledQueue is drained check the
     13             mDiscoveredList to see if any item has a Depex that is ready to
     14             be placed on the mScheduledQueue.
     15 
     16   Step #3 - Adding to the mScheduledQueue requires that you process Before
     17             and After dependencies. This is done recursively as the call to add
     18             to the mScheduledQueue checks for Before and recursively adds
     19             all Befores. It then addes the item that was passed in and then
     20             processess the After dependecies by recursively calling the routine.
     21 
     22   Dispatcher Rules:
     23   The rules for the dispatcher are similar to the DXE dispatcher.
     24 
     25   The rules for DXE dispatcher are in chapter 10 of the DXE CIS. Figure 10-3
     26   is the state diagram for the DXE dispatcher
     27 
     28   Depex - Dependency Expresion.
     29 
     30   Copyright (c) 2014, Hewlett-Packard Development Company, L.P.
     31   Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
     32   This program and the accompanying materials are licensed and made available
     33   under the terms and conditions of the BSD License which accompanies this
     34   distribution.  The full text of the license may be found at
     35   http://opensource.org/licenses/bsd-license.php
     36 
     37   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     38   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     39 
     40 **/
     41 
     42 #include "PiSmmCore.h"
     43 
     44 //
     45 // SMM Dispatcher Data structures
     46 //
     47 #define KNOWN_HANDLE_SIGNATURE  SIGNATURE_32('k','n','o','w')
     48 typedef struct {
     49   UINTN           Signature;
     50   LIST_ENTRY      Link;         // mFvHandleList
     51   EFI_HANDLE      Handle;
     52 } KNOWN_HANDLE;
     53 
     54 //
     55 // Function Prototypes
     56 //
     57 
     58 /**
     59   Insert InsertedDriverEntry onto the mScheduledQueue. To do this you
     60   must add any driver with a before dependency on InsertedDriverEntry first.
     61   You do this by recursively calling this routine. After all the Befores are
     62   processed you can add InsertedDriverEntry to the mScheduledQueue.
     63   Then you can add any driver with an After dependency on InsertedDriverEntry
     64   by recursively calling this routine.
     65 
     66   @param  InsertedDriverEntry   The driver to insert on the ScheduledLink Queue
     67 
     68 **/
     69 VOID
     70 SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (
     71   IN  EFI_SMM_DRIVER_ENTRY   *InsertedDriverEntry
     72   );
     73 
     74 //
     75 // The Driver List contains one copy of every driver that has been discovered.
     76 // Items are never removed from the driver list. List of EFI_SMM_DRIVER_ENTRY
     77 //
     78 LIST_ENTRY  mDiscoveredList = INITIALIZE_LIST_HEAD_VARIABLE (mDiscoveredList);
     79 
     80 //
     81 // Queue of drivers that are ready to dispatch. This queue is a subset of the
     82 // mDiscoveredList.list of EFI_SMM_DRIVER_ENTRY.
     83 //
     84 LIST_ENTRY  mScheduledQueue = INITIALIZE_LIST_HEAD_VARIABLE (mScheduledQueue);
     85 
     86 //
     87 // List of handles who's Fv's have been parsed and added to the mFwDriverList.
     88 //
     89 LIST_ENTRY  mFvHandleList = INITIALIZE_LIST_HEAD_VARIABLE (mFvHandleList);
     90 
     91 //
     92 // Flag for the SMM Dispacher.  TRUE if dispatcher is execuing.
     93 //
     94 BOOLEAN  gDispatcherRunning = FALSE;
     95 
     96 //
     97 // Flag for the SMM Dispacher.  TRUE if there is one or more SMM drivers ready to be dispatched
     98 //
     99 BOOLEAN  gRequestDispatch = FALSE;
    100 
    101 //
    102 // List of file types supported by dispatcher
    103 //
    104 EFI_FV_FILETYPE mSmmFileTypes[] = {
    105   EFI_FV_FILETYPE_SMM,
    106   EFI_FV_FILETYPE_COMBINED_SMM_DXE,
    107   EFI_FV_FILETYPE_SMM_CORE,
    108   //
    109   // Note: DXE core will process the FV image file, so skip it in SMM core
    110   // EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE
    111   //
    112 };
    113 
    114 typedef struct {
    115   MEDIA_FW_VOL_FILEPATH_DEVICE_PATH  File;
    116   EFI_DEVICE_PATH_PROTOCOL           End;
    117 } FV_FILEPATH_DEVICE_PATH;
    118 
    119 FV_FILEPATH_DEVICE_PATH  mFvDevicePath;
    120 
    121 //
    122 // DXE Architecture Protocols
    123 //
    124 EFI_SECURITY_ARCH_PROTOCOL  *mSecurity = NULL;
    125 EFI_SECURITY2_ARCH_PROTOCOL *mSecurity2 = NULL;
    126 
    127 //
    128 // The global variable is defined for Loading modules at fixed address feature to track the SMM code
    129 // memory range usage. It is a bit mapped array in which every bit indicates the corresponding
    130 // memory page available or not.
    131 //
    132 GLOBAL_REMOVE_IF_UNREFERENCED    UINT64                *mSmmCodeMemoryRangeUsageBitMap=NULL;
    133 
    134 /**
    135   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
    136   memory range is available, the function will mark the corresponding bits to 1 which indicates the memory range is used.
    137   The function is only invoked when load modules at fixed address feature is enabled.
    138 
    139   @param  ImageBase                The base address the image will be loaded at.
    140   @param  ImageSize                The size of the image
    141 
    142   @retval EFI_SUCCESS              The memory range the image will be loaded in is available
    143   @retval EFI_NOT_FOUND            The memory range the image will be loaded in is not available
    144 **/
    145 EFI_STATUS
    146 CheckAndMarkFixLoadingMemoryUsageBitMap (
    147   IN  EFI_PHYSICAL_ADDRESS          ImageBase,
    148   IN  UINTN                         ImageSize
    149   )
    150 {
    151    UINT32                             SmmCodePageNumber;
    152    UINT64                             SmmCodeSize;
    153    EFI_PHYSICAL_ADDRESS               SmmCodeBase;
    154    UINTN                              BaseOffsetPageNumber;
    155    UINTN                              TopOffsetPageNumber;
    156    UINTN                              Index;
    157    //
    158    // Build tool will calculate the smm code size and then patch the PcdLoadFixAddressSmmCodePageNumber
    159    //
    160    SmmCodePageNumber = PcdGet32(PcdLoadFixAddressSmmCodePageNumber);
    161    SmmCodeSize = EFI_PAGES_TO_SIZE (SmmCodePageNumber);
    162    SmmCodeBase = gLoadModuleAtFixAddressSmramBase;
    163 
    164    //
    165    // If the memory usage bit map is not initialized,  do it. Every bit in the array
    166    // indicate the status of the corresponding memory page, available or not
    167    //
    168    if (mSmmCodeMemoryRangeUsageBitMap == NULL) {
    169      mSmmCodeMemoryRangeUsageBitMap = AllocateZeroPool(((SmmCodePageNumber / 64) + 1)*sizeof(UINT64));
    170    }
    171    //
    172    // If the Dxe code memory range is not allocated or the bit map array allocation failed, return EFI_NOT_FOUND
    173    //
    174    if (mSmmCodeMemoryRangeUsageBitMap == NULL) {
    175      return EFI_NOT_FOUND;
    176    }
    177    //
    178    // see if the memory range for loading the image is in the SMM code range.
    179    //
    180    if (SmmCodeBase + SmmCodeSize <  ImageBase + ImageSize || SmmCodeBase >  ImageBase) {
    181      return EFI_NOT_FOUND;
    182    }
    183    //
    184    // Test if the memory is avalaible or not.
    185    //
    186    BaseOffsetPageNumber = (UINTN)EFI_SIZE_TO_PAGES((UINT32)(ImageBase - SmmCodeBase));
    187    TopOffsetPageNumber  = (UINTN)EFI_SIZE_TO_PAGES((UINT32)(ImageBase + ImageSize - SmmCodeBase));
    188    for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index ++) {
    189      if ((mSmmCodeMemoryRangeUsageBitMap[Index / 64] & LShiftU64(1, (Index % 64))) != 0) {
    190        //
    191        // This page is already used.
    192        //
    193        return EFI_NOT_FOUND;
    194      }
    195    }
    196 
    197    //
    198    // Being here means the memory range is available.  So mark the bits for the memory range
    199    //
    200    for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index ++) {
    201      mSmmCodeMemoryRangeUsageBitMap[Index / 64] |= LShiftU64(1, (Index % 64));
    202    }
    203    return  EFI_SUCCESS;
    204 }
    205 /**
    206   Get the fixed loading address from image header assigned by build tool. This function only be called
    207   when Loading module at Fixed address feature enabled.
    208 
    209   @param  ImageContext              Pointer to the image context structure that describes the PE/COFF
    210                                     image that needs to be examined by this function.
    211   @retval EFI_SUCCESS               An fixed loading address is assigned to this image by build tools .
    212   @retval EFI_NOT_FOUND             The image has no assigned fixed loading address.
    213 
    214 **/
    215 EFI_STATUS
    216 GetPeCoffImageFixLoadingAssignedAddress(
    217   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
    218   )
    219 {
    220   UINTN                              SectionHeaderOffset;
    221   EFI_STATUS                         Status;
    222   EFI_IMAGE_SECTION_HEADER           SectionHeader;
    223   EFI_IMAGE_OPTIONAL_HEADER_UNION    *ImgHdr;
    224   EFI_PHYSICAL_ADDRESS               FixLoadingAddress;
    225   UINT16                             Index;
    226   UINTN                              Size;
    227   UINT16                             NumberOfSections;
    228   UINT64                             ValueInSectionHeader;
    229 
    230   FixLoadingAddress = 0;
    231   Status = EFI_NOT_FOUND;
    232 
    233   //
    234   // Get PeHeader pointer
    235   //
    236   ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((CHAR8* )ImageContext->Handle + ImageContext->PeCoffHeaderOffset);
    237   SectionHeaderOffset = (UINTN)(
    238                                  ImageContext->PeCoffHeaderOffset +
    239                                  sizeof (UINT32) +
    240                                  sizeof (EFI_IMAGE_FILE_HEADER) +
    241                                  ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader
    242                                  );
    243   NumberOfSections = ImgHdr->Pe32.FileHeader.NumberOfSections;
    244 
    245   //
    246   // Get base address from the first section header that doesn't point to code section.
    247   //
    248   for (Index = 0; Index < NumberOfSections; Index++) {
    249     //
    250     // Read section header from file
    251     //
    252     Size = sizeof (EFI_IMAGE_SECTION_HEADER);
    253     Status = ImageContext->ImageRead (
    254                               ImageContext->Handle,
    255                               SectionHeaderOffset,
    256                               &Size,
    257                               &SectionHeader
    258                               );
    259     if (EFI_ERROR (Status)) {
    260       return Status;
    261     }
    262 
    263     Status = EFI_NOT_FOUND;
    264 
    265     if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_CNT_CODE) == 0) {
    266       //
    267       // Build tool will save the address in PointerToRelocations & PointerToLineNumbers fields in the first section header
    268       // that doesn't point to code section in image header.So there is an assumption that when the feature is enabled,
    269       // if a module with a loading address assigned by tools, the PointerToRelocations & PointerToLineNumbers fields
    270       // should not be Zero, or else, these 2 fields should be set to Zero
    271       //
    272       ValueInSectionHeader = ReadUnaligned64((UINT64*)&SectionHeader.PointerToRelocations);
    273       if (ValueInSectionHeader != 0) {
    274         //
    275         // Found first section header that doesn't point to code section in which build tool saves the
    276         // offset to SMRAM base as image base in PointerToRelocations & PointerToLineNumbers fields
    277         //
    278         FixLoadingAddress = (EFI_PHYSICAL_ADDRESS)(gLoadModuleAtFixAddressSmramBase + (INT64)ValueInSectionHeader);
    279         //
    280         // Check if the memory range is available.
    281         //
    282         Status = CheckAndMarkFixLoadingMemoryUsageBitMap (FixLoadingAddress, (UINTN)(ImageContext->ImageSize + ImageContext->SectionAlignment));
    283         if (!EFI_ERROR(Status)) {
    284           //
    285           // The assigned address is valid. Return the specified loading address
    286           //
    287           ImageContext->ImageAddress = FixLoadingAddress;
    288         }
    289       }
    290       break;
    291     }
    292     SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
    293   }
    294   DEBUG ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address %x, Status = %r\n", FixLoadingAddress, Status));
    295   return Status;
    296 }
    297 /**
    298   Loads an EFI image into SMRAM.
    299 
    300   @param  DriverEntry             EFI_SMM_DRIVER_ENTRY instance
    301 
    302   @return EFI_STATUS
    303 
    304 **/
    305 EFI_STATUS
    306 EFIAPI
    307 SmmLoadImage (
    308   IN OUT EFI_SMM_DRIVER_ENTRY  *DriverEntry
    309   )
    310 {
    311   UINT32                         AuthenticationStatus;
    312   UINTN                          FilePathSize;
    313   VOID                           *Buffer;
    314   UINTN                          Size;
    315   UINTN                          PageCount;
    316   EFI_GUID                       *NameGuid;
    317   EFI_STATUS                     Status;
    318   EFI_STATUS                     SecurityStatus;
    319   EFI_HANDLE                     DeviceHandle;
    320   EFI_PHYSICAL_ADDRESS           DstBuffer;
    321   EFI_DEVICE_PATH_PROTOCOL       *FilePath;
    322   EFI_DEVICE_PATH_PROTOCOL       *OriginalFilePath;
    323   EFI_DEVICE_PATH_PROTOCOL       *HandleFilePath;
    324   EFI_FIRMWARE_VOLUME2_PROTOCOL  *Fv;
    325   PE_COFF_LOADER_IMAGE_CONTEXT   ImageContext;
    326   UINT64                         Tick;
    327 
    328   Tick = 0;
    329   PERF_CODE (
    330     Tick = GetPerformanceCounter ();
    331   );
    332 
    333   Buffer               = NULL;
    334   Size                 = 0;
    335   Fv                   = DriverEntry->Fv;
    336   NameGuid             = &DriverEntry->FileName;
    337   FilePath             = DriverEntry->FvFileDevicePath;
    338 
    339   OriginalFilePath     = FilePath;
    340   HandleFilePath       = FilePath;
    341   DeviceHandle         = NULL;
    342   SecurityStatus       = EFI_SUCCESS;
    343   Status               = EFI_SUCCESS;
    344   AuthenticationStatus = 0;
    345 
    346   //
    347   // Try to get the image device handle by checking the match protocol.
    348   //
    349   Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &HandleFilePath, &DeviceHandle);
    350   if (EFI_ERROR(Status)) {
    351     return Status;
    352   }
    353 
    354   //
    355   // If the Security2 and Security Architectural Protocol has not been located yet, then attempt to locate it
    356   //
    357   if (mSecurity2 == NULL) {
    358     gBS->LocateProtocol (&gEfiSecurity2ArchProtocolGuid, NULL, (VOID**)&mSecurity2);
    359   }
    360   if (mSecurity == NULL) {
    361     gBS->LocateProtocol (&gEfiSecurityArchProtocolGuid, NULL, (VOID**)&mSecurity);
    362   }
    363   //
    364   // When Security2 is installed, Security Architectural Protocol must be published.
    365   //
    366   ASSERT (mSecurity2 == NULL || mSecurity != NULL);
    367 
    368   //
    369   // Pull out just the file portion of the DevicePath for the LoadedImage FilePath
    370   //
    371   FilePath = OriginalFilePath;
    372   Status = gBS->HandleProtocol (DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID **)&HandleFilePath);
    373   if (!EFI_ERROR (Status)) {
    374     FilePathSize = GetDevicePathSize (HandleFilePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL);
    375     FilePath = (EFI_DEVICE_PATH_PROTOCOL *) (((UINT8 *)FilePath) + FilePathSize );
    376   }
    377 
    378   //
    379   // Try reading PE32 section firstly
    380   //
    381   Status = Fv->ReadSection (
    382                  Fv,
    383                  NameGuid,
    384                  EFI_SECTION_PE32,
    385                  0,
    386                  &Buffer,
    387                  &Size,
    388                  &AuthenticationStatus
    389                  );
    390 
    391   if (EFI_ERROR (Status)) {
    392     //
    393     // Try reading TE section secondly
    394     //
    395     Buffer = NULL;
    396     Size   = 0;
    397     Status = Fv->ReadSection (
    398                   Fv,
    399                   NameGuid,
    400                   EFI_SECTION_TE,
    401                   0,
    402                   &Buffer,
    403                   &Size,
    404                   &AuthenticationStatus
    405                   );
    406   }
    407 
    408   if (EFI_ERROR (Status)) {
    409     if (Buffer != NULL) {
    410       gBS->FreePool (Buffer);
    411     }
    412     return Status;
    413   }
    414 
    415   //
    416   // Verify File Authentication through the Security2 Architectural Protocol
    417   //
    418   if (mSecurity2 != NULL) {
    419     SecurityStatus = mSecurity2->FileAuthentication (
    420                                   mSecurity2,
    421                                   OriginalFilePath,
    422                                   Buffer,
    423                                   Size,
    424                                   FALSE
    425                                   );
    426   }
    427 
    428   //
    429   // Verify the Authentication Status through the Security Architectural Protocol
    430   // Only on images that have been read using Firmware Volume protocol.
    431   // All SMM images are from FV protocol.
    432   //
    433   if (!EFI_ERROR (SecurityStatus) && (mSecurity != NULL)) {
    434     SecurityStatus = mSecurity->FileAuthenticationState (
    435                                   mSecurity,
    436                                   AuthenticationStatus,
    437                                   OriginalFilePath
    438                                   );
    439   }
    440 
    441   if (EFI_ERROR (SecurityStatus) && SecurityStatus != EFI_SECURITY_VIOLATION) {
    442     Status = SecurityStatus;
    443     return Status;
    444   }
    445 
    446   //
    447   // Initialize ImageContext
    448   //
    449   ImageContext.Handle = Buffer;
    450   ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
    451 
    452   //
    453   // Get information about the image being loaded
    454   //
    455   Status = PeCoffLoaderGetImageInfo (&ImageContext);
    456   if (EFI_ERROR (Status)) {
    457     if (Buffer != NULL) {
    458       gBS->FreePool (Buffer);
    459     }
    460     return Status;
    461   }
    462   //
    463   // if Loading module at Fixed Address feature is enabled, then  cut out a memory range started from TESG BASE
    464   // to hold the Smm driver code
    465   //
    466   if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) {
    467     //
    468     // Get the fixed loading address assigned by Build tool
    469     //
    470     Status = GetPeCoffImageFixLoadingAssignedAddress (&ImageContext);
    471     if (!EFI_ERROR (Status)) {
    472       //
    473       // Since the memory range to load Smm core alreay been cut out, so no need to allocate and free this range
    474       // following statements is to bypass SmmFreePages
    475       //
    476       PageCount = 0;
    477       DstBuffer = (UINTN)gLoadModuleAtFixAddressSmramBase;
    478     } else {
    479        DEBUG ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED ERROR: Failed to load module at fixed address. \n"));
    480        //
    481        // allocate the memory to load the SMM driver
    482        //
    483        PageCount = (UINTN)EFI_SIZE_TO_PAGES((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment);
    484        DstBuffer = (UINTN)(-1);
    485 
    486        Status = SmmAllocatePages (
    487                    AllocateMaxAddress,
    488                    EfiRuntimeServicesCode,
    489                    PageCount,
    490                    &DstBuffer
    491                    );
    492        if (EFI_ERROR (Status)) {
    493          if (Buffer != NULL) {
    494            gBS->FreePool (Buffer);
    495          }
    496          return Status;
    497        }
    498       ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer;
    499     }
    500   } else {
    501      PageCount = (UINTN)EFI_SIZE_TO_PAGES((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment);
    502      DstBuffer = (UINTN)(-1);
    503 
    504      Status = SmmAllocatePages (
    505                   AllocateMaxAddress,
    506                   EfiRuntimeServicesCode,
    507                   PageCount,
    508                   &DstBuffer
    509                   );
    510      if (EFI_ERROR (Status)) {
    511        if (Buffer != NULL) {
    512          gBS->FreePool (Buffer);
    513        }
    514        return Status;
    515      }
    516 
    517      ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer;
    518   }
    519   //
    520   // Align buffer on section boundary
    521   //
    522   ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;
    523   ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext.SectionAlignment - 1));
    524 
    525   //
    526   // Load the image to our new buffer
    527   //
    528   Status = PeCoffLoaderLoadImage (&ImageContext);
    529   if (EFI_ERROR (Status)) {
    530     if (Buffer != NULL) {
    531       gBS->FreePool (Buffer);
    532     }
    533     SmmFreePages (DstBuffer, PageCount);
    534     return Status;
    535   }
    536 
    537   //
    538   // Relocate the image in our new buffer
    539   //
    540   Status = PeCoffLoaderRelocateImage (&ImageContext);
    541   if (EFI_ERROR (Status)) {
    542     if (Buffer != NULL) {
    543       gBS->FreePool (Buffer);
    544     }
    545     SmmFreePages (DstBuffer, PageCount);
    546     return Status;
    547   }
    548 
    549   //
    550   // Flush the instruction cache so the image data are written before we execute it
    551   //
    552   InvalidateInstructionCacheRange ((VOID *)(UINTN) ImageContext.ImageAddress, (UINTN) ImageContext.ImageSize);
    553 
    554   //
    555   // Save Image EntryPoint in DriverEntry
    556   //
    557   DriverEntry->ImageEntryPoint  = ImageContext.EntryPoint;
    558   DriverEntry->ImageBuffer      = DstBuffer;
    559   DriverEntry->NumberOfPage     = PageCount;
    560 
    561   //
    562   // Allocate a Loaded Image Protocol in EfiBootServicesData
    563   //
    564   Status = gBS->AllocatePool (EfiBootServicesData, sizeof (EFI_LOADED_IMAGE_PROTOCOL), (VOID **)&DriverEntry->LoadedImage);
    565   if (EFI_ERROR (Status)) {
    566     if (Buffer != NULL) {
    567       gBS->FreePool (Buffer);
    568     }
    569     SmmFreePages (DstBuffer, PageCount);
    570     return Status;
    571   }
    572 
    573   ZeroMem (DriverEntry->LoadedImage, sizeof (EFI_LOADED_IMAGE_PROTOCOL));
    574   //
    575   // Fill in the remaining fields of the Loaded Image Protocol instance.
    576   // Note: ImageBase is an SMRAM address that can not be accessed outside of SMRAM if SMRAM window is closed.
    577   //
    578   DriverEntry->LoadedImage->Revision      = EFI_LOADED_IMAGE_PROTOCOL_REVISION;
    579   DriverEntry->LoadedImage->ParentHandle  = gSmmCorePrivate->SmmIplImageHandle;
    580   DriverEntry->LoadedImage->SystemTable   = gST;
    581   DriverEntry->LoadedImage->DeviceHandle  = DeviceHandle;
    582 
    583   DriverEntry->SmmLoadedImage.Revision     = EFI_LOADED_IMAGE_PROTOCOL_REVISION;
    584   DriverEntry->SmmLoadedImage.ParentHandle = gSmmCorePrivate->SmmIplImageHandle;
    585   DriverEntry->SmmLoadedImage.SystemTable  = gST;
    586   DriverEntry->SmmLoadedImage.DeviceHandle = DeviceHandle;
    587 
    588   //
    589   // Make an EfiBootServicesData buffer copy of FilePath
    590   //
    591   Status = gBS->AllocatePool (EfiBootServicesData, GetDevicePathSize (FilePath), (VOID **)&DriverEntry->LoadedImage->FilePath);
    592   if (EFI_ERROR (Status)) {
    593     if (Buffer != NULL) {
    594       gBS->FreePool (Buffer);
    595     }
    596     SmmFreePages (DstBuffer, PageCount);
    597     return Status;
    598   }
    599   CopyMem (DriverEntry->LoadedImage->FilePath, FilePath, GetDevicePathSize (FilePath));
    600 
    601   DriverEntry->LoadedImage->ImageBase     = (VOID *)(UINTN)DriverEntry->ImageBuffer;
    602   DriverEntry->LoadedImage->ImageSize     = ImageContext.ImageSize;
    603   DriverEntry->LoadedImage->ImageCodeType = EfiRuntimeServicesCode;
    604   DriverEntry->LoadedImage->ImageDataType = EfiRuntimeServicesData;
    605 
    606   //
    607   // Make a buffer copy of FilePath
    608   //
    609   Status = SmmAllocatePool (EfiRuntimeServicesData, GetDevicePathSize(FilePath), (VOID **)&DriverEntry->SmmLoadedImage.FilePath);
    610   if (EFI_ERROR (Status)) {
    611     if (Buffer != NULL) {
    612       gBS->FreePool (Buffer);
    613     }
    614     gBS->FreePool (DriverEntry->LoadedImage->FilePath);
    615     SmmFreePages (DstBuffer, PageCount);
    616     return Status;
    617   }
    618   CopyMem (DriverEntry->SmmLoadedImage.FilePath, FilePath, GetDevicePathSize(FilePath));
    619 
    620   DriverEntry->SmmLoadedImage.ImageBase = (VOID *)(UINTN)DriverEntry->ImageBuffer;
    621   DriverEntry->SmmLoadedImage.ImageSize = ImageContext.ImageSize;
    622   DriverEntry->SmmLoadedImage.ImageCodeType = EfiRuntimeServicesCode;
    623   DriverEntry->SmmLoadedImage.ImageDataType = EfiRuntimeServicesData;
    624 
    625   //
    626   // Create a new image handle in the UEFI handle database for the SMM Driver
    627   //
    628   DriverEntry->ImageHandle = NULL;
    629   Status = gBS->InstallMultipleProtocolInterfaces (
    630                   &DriverEntry->ImageHandle,
    631                   &gEfiLoadedImageProtocolGuid, DriverEntry->LoadedImage,
    632                   NULL
    633                   );
    634 
    635   //
    636   // Create a new image handle in the SMM handle database for the SMM Driver
    637   //
    638   DriverEntry->SmmImageHandle = NULL;
    639   Status = SmmInstallProtocolInterface (
    640              &DriverEntry->SmmImageHandle,
    641              &gEfiLoadedImageProtocolGuid,
    642              EFI_NATIVE_INTERFACE,
    643              &DriverEntry->SmmLoadedImage
    644              );
    645 
    646   PERF_START (DriverEntry->ImageHandle, "LoadImage:", NULL, Tick);
    647   PERF_END (DriverEntry->ImageHandle, "LoadImage:", NULL, 0);
    648 
    649   //
    650   // Print the load address and the PDB file name if it is available
    651   //
    652 
    653   DEBUG_CODE_BEGIN ();
    654 
    655     UINTN Index;
    656     UINTN StartIndex;
    657     CHAR8 EfiFileName[256];
    658 
    659 
    660     DEBUG ((DEBUG_INFO | DEBUG_LOAD,
    661            "Loading SMM driver at 0x%11p EntryPoint=0x%11p ",
    662            (VOID *)(UINTN) ImageContext.ImageAddress,
    663            FUNCTION_ENTRY_POINT (ImageContext.EntryPoint)));
    664 
    665 
    666     //
    667     // Print Module Name by Pdb file path.
    668     // Windows and Unix style file path are all trimmed correctly.
    669     //
    670     if (ImageContext.PdbPointer != NULL) {
    671       StartIndex = 0;
    672       for (Index = 0; ImageContext.PdbPointer[Index] != 0; Index++) {
    673         if ((ImageContext.PdbPointer[Index] == '\\') || (ImageContext.PdbPointer[Index] == '/')) {
    674           StartIndex = Index + 1;
    675         }
    676       }
    677       //
    678       // Copy the PDB file name to our temporary string, and replace .pdb with .efi
    679       // The PDB file name is limited in the range of 0~255.
    680       // If the length is bigger than 255, trim the redudant characters to avoid overflow in array boundary.
    681       //
    682       for (Index = 0; Index < sizeof (EfiFileName) - 4; Index++) {
    683         EfiFileName[Index] = ImageContext.PdbPointer[Index + StartIndex];
    684         if (EfiFileName[Index] == 0) {
    685           EfiFileName[Index] = '.';
    686         }
    687         if (EfiFileName[Index] == '.') {
    688           EfiFileName[Index + 1] = 'e';
    689           EfiFileName[Index + 2] = 'f';
    690           EfiFileName[Index + 3] = 'i';
    691           EfiFileName[Index + 4] = 0;
    692           break;
    693         }
    694       }
    695 
    696       if (Index == sizeof (EfiFileName) - 4) {
    697         EfiFileName[Index] = 0;
    698       }
    699       DEBUG ((DEBUG_INFO | DEBUG_LOAD, "%a", EfiFileName)); // &Image->ImageContext.PdbPointer[StartIndex]));
    700     }
    701     DEBUG ((DEBUG_INFO | DEBUG_LOAD, "\n"));
    702 
    703   DEBUG_CODE_END ();
    704 
    705   //
    706   // Free buffer allocated by Fv->ReadSection.
    707   //
    708   // The UEFI Boot Services FreePool() function must be used because Fv->ReadSection
    709   // used the UEFI Boot Services AllocatePool() function
    710   //
    711   Status = gBS->FreePool(Buffer);
    712   if (!EFI_ERROR (Status) && EFI_ERROR (SecurityStatus)) {
    713     Status = SecurityStatus;
    714   }
    715   return Status;
    716 }
    717 
    718 /**
    719   Preprocess dependency expression and update DriverEntry to reflect the
    720   state of  Before and After dependencies. If DriverEntry->Before
    721   or DriverEntry->After is set it will never be cleared.
    722 
    723   @param  DriverEntry           DriverEntry element to update .
    724 
    725   @retval EFI_SUCCESS           It always works.
    726 
    727 **/
    728 EFI_STATUS
    729 SmmPreProcessDepex (
    730   IN EFI_SMM_DRIVER_ENTRY  *DriverEntry
    731   )
    732 {
    733   UINT8  *Iterator;
    734 
    735   Iterator = DriverEntry->Depex;
    736   DriverEntry->Dependent = TRUE;
    737 
    738   if (*Iterator == EFI_DEP_BEFORE) {
    739     DriverEntry->Before = TRUE;
    740   } else if (*Iterator == EFI_DEP_AFTER) {
    741     DriverEntry->After = TRUE;
    742   }
    743 
    744   if (DriverEntry->Before || DriverEntry->After) {
    745     CopyMem (&DriverEntry->BeforeAfterGuid, Iterator + 1, sizeof (EFI_GUID));
    746   }
    747 
    748   return EFI_SUCCESS;
    749 }
    750 
    751 /**
    752   Read Depex and pre-process the Depex for Before and After. If Section Extraction
    753   protocol returns an error via ReadSection defer the reading of the Depex.
    754 
    755   @param  DriverEntry           Driver to work on.
    756 
    757   @retval EFI_SUCCESS           Depex read and preprossesed
    758   @retval EFI_PROTOCOL_ERROR    The section extraction protocol returned an error
    759                                 and  Depex reading needs to be retried.
    760   @retval Error                 DEPEX not found.
    761 
    762 **/
    763 EFI_STATUS
    764 SmmGetDepexSectionAndPreProccess (
    765   IN EFI_SMM_DRIVER_ENTRY  *DriverEntry
    766   )
    767 {
    768   EFI_STATUS                     Status;
    769   EFI_SECTION_TYPE               SectionType;
    770   UINT32                         AuthenticationStatus;
    771   EFI_FIRMWARE_VOLUME2_PROTOCOL  *Fv;
    772 
    773   Fv = DriverEntry->Fv;
    774 
    775   //
    776   // Grab Depex info, it will never be free'ed.
    777   // (Note: DriverEntry->Depex is in DXE memory)
    778   //
    779   SectionType         = EFI_SECTION_SMM_DEPEX;
    780   Status = Fv->ReadSection (
    781                 DriverEntry->Fv,
    782                 &DriverEntry->FileName,
    783                 SectionType,
    784                 0,
    785                 &DriverEntry->Depex,
    786                 (UINTN *)&DriverEntry->DepexSize,
    787                 &AuthenticationStatus
    788                 );
    789   if (EFI_ERROR (Status)) {
    790     if (Status == EFI_PROTOCOL_ERROR) {
    791       //
    792       // The section extraction protocol failed so set protocol error flag
    793       //
    794       DriverEntry->DepexProtocolError = TRUE;
    795     } else {
    796       //
    797       // If no Depex assume depend on all architectural protocols
    798       //
    799       DriverEntry->Depex = NULL;
    800       DriverEntry->Dependent = TRUE;
    801       DriverEntry->DepexProtocolError = FALSE;
    802     }
    803   } else {
    804     //
    805     // Set Before and After state information based on Depex
    806     // Driver will be put in Dependent state
    807     //
    808     SmmPreProcessDepex (DriverEntry);
    809     DriverEntry->DepexProtocolError = FALSE;
    810   }
    811 
    812   return Status;
    813 }
    814 
    815 /**
    816   This is the main Dispatcher for SMM and it exits when there are no more
    817   drivers to run. Drain the mScheduledQueue and load and start a PE
    818   image for each driver. Search the mDiscoveredList to see if any driver can
    819   be placed on the mScheduledQueue. If no drivers are placed on the
    820   mScheduledQueue exit the function.
    821 
    822   @retval EFI_SUCCESS           All of the SMM Drivers that could be dispatched
    823                                 have been run and the SMM Entry Point has been
    824                                 registered.
    825   @retval EFI_NOT_READY         The SMM Driver that registered the SMM Entry Point
    826                                 was just dispatched.
    827   @retval EFI_NOT_FOUND         There are no SMM Drivers available to be dispatched.
    828   @retval EFI_ALREADY_STARTED   The SMM Dispatcher is already running
    829 
    830 **/
    831 EFI_STATUS
    832 SmmDispatcher (
    833   VOID
    834   )
    835 {
    836   EFI_STATUS            Status;
    837   LIST_ENTRY            *Link;
    838   EFI_SMM_DRIVER_ENTRY  *DriverEntry;
    839   BOOLEAN               ReadyToRun;
    840   BOOLEAN               PreviousSmmEntryPointRegistered;
    841 
    842   if (!gRequestDispatch) {
    843     return EFI_NOT_FOUND;
    844   }
    845 
    846   if (gDispatcherRunning) {
    847     //
    848     // If the dispatcher is running don't let it be restarted.
    849     //
    850     return EFI_ALREADY_STARTED;
    851   }
    852 
    853   gDispatcherRunning = TRUE;
    854 
    855   do {
    856     //
    857     // Drain the Scheduled Queue
    858     //
    859     while (!IsListEmpty (&mScheduledQueue)) {
    860       DriverEntry = CR (
    861                       mScheduledQueue.ForwardLink,
    862                       EFI_SMM_DRIVER_ENTRY,
    863                       ScheduledLink,
    864                       EFI_SMM_DRIVER_ENTRY_SIGNATURE
    865                       );
    866 
    867       //
    868       // Load the SMM Driver image into memory. If the Driver was transitioned from
    869       // Untrused to Scheduled it would have already been loaded so we may need to
    870       // skip the LoadImage
    871       //
    872       if (DriverEntry->ImageHandle == NULL) {
    873         Status = SmmLoadImage (DriverEntry);
    874 
    875         //
    876         // Update the driver state to reflect that it's been loaded
    877         //
    878         if (EFI_ERROR (Status)) {
    879           //
    880           // The SMM Driver could not be loaded, and do not attempt to load or start it again.
    881           // Take driver from Scheduled to Initialized.
    882           //
    883           DriverEntry->Initialized  = TRUE;
    884           DriverEntry->Scheduled = FALSE;
    885           RemoveEntryList (&DriverEntry->ScheduledLink);
    886 
    887           //
    888           // If it's an error don't try the StartImage
    889           //
    890           continue;
    891         }
    892       }
    893 
    894       DriverEntry->Scheduled    = FALSE;
    895       DriverEntry->Initialized  = TRUE;
    896       RemoveEntryList (&DriverEntry->ScheduledLink);
    897 
    898       REPORT_STATUS_CODE_WITH_EXTENDED_DATA (
    899         EFI_PROGRESS_CODE,
    900         EFI_SOFTWARE_SMM_DRIVER | EFI_SW_PC_INIT_BEGIN,
    901         &DriverEntry->ImageHandle,
    902         sizeof (DriverEntry->ImageHandle)
    903         );
    904 
    905       //
    906       // Cache state of SmmEntryPointRegistered before calling entry point
    907       //
    908       PreviousSmmEntryPointRegistered = gSmmCorePrivate->SmmEntryPointRegistered;
    909 
    910       //
    911       // For each SMM driver, pass NULL as ImageHandle
    912       //
    913       RegisterSmramProfileImage (DriverEntry, TRUE);
    914       PERF_START (DriverEntry->ImageHandle, "StartImage:", NULL, 0);
    915       Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)DriverEntry->ImageEntryPoint)(DriverEntry->ImageHandle, gST);
    916       PERF_END (DriverEntry->ImageHandle, "StartImage:", NULL, 0);
    917       if (EFI_ERROR(Status)){
    918         UnregisterSmramProfileImage (DriverEntry, TRUE);
    919         SmmFreePages(DriverEntry->ImageBuffer, DriverEntry->NumberOfPage);
    920         //
    921         // Uninstall LoadedImage
    922         //
    923         Status = gBS->UninstallProtocolInterface (
    924                         DriverEntry->ImageHandle,
    925                         &gEfiLoadedImageProtocolGuid,
    926                         DriverEntry->LoadedImage
    927                         );
    928         if (!EFI_ERROR (Status)) {
    929           if (DriverEntry->LoadedImage->FilePath != NULL) {
    930             gBS->FreePool (DriverEntry->LoadedImage->FilePath);
    931           }
    932           gBS->FreePool (DriverEntry->LoadedImage);
    933         }
    934         Status = SmmUninstallProtocolInterface (
    935                    DriverEntry->SmmImageHandle,
    936                    &gEfiLoadedImageProtocolGuid,
    937                    &DriverEntry->SmmLoadedImage
    938                    );
    939         if (!EFI_ERROR(Status)) {
    940           if (DriverEntry->SmmLoadedImage.FilePath != NULL) {
    941             SmmFreePool (DriverEntry->SmmLoadedImage.FilePath);
    942           }
    943         }
    944       }
    945 
    946       REPORT_STATUS_CODE_WITH_EXTENDED_DATA (
    947         EFI_PROGRESS_CODE,
    948         EFI_SOFTWARE_SMM_DRIVER | EFI_SW_PC_INIT_END,
    949         &DriverEntry->ImageHandle,
    950         sizeof (DriverEntry->ImageHandle)
    951         );
    952 
    953       if (!PreviousSmmEntryPointRegistered && gSmmCorePrivate->SmmEntryPointRegistered) {
    954         //
    955         // Return immediately if the SMM Entry Point was registered by the SMM
    956         // Driver that was just dispatched.  The SMM IPL will reinvoke the SMM
    957         // Core Dispatcher.  This is required so SMM Mode may be enabled as soon
    958         // as all the dependent SMM Drivers for SMM Mode have been dispatched.
    959         // Once the SMM Entry Point has been registered, then SMM Mode will be
    960         // used.
    961         //
    962         gRequestDispatch = TRUE;
    963         gDispatcherRunning = FALSE;
    964         return EFI_NOT_READY;
    965       }
    966     }
    967 
    968     //
    969     // Search DriverList for items to place on Scheduled Queue
    970     //
    971     ReadyToRun = FALSE;
    972     for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {
    973       DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE);
    974 
    975       if (DriverEntry->DepexProtocolError){
    976         //
    977         // If Section Extraction Protocol did not let the Depex be read before retry the read
    978         //
    979         Status = SmmGetDepexSectionAndPreProccess (DriverEntry);
    980       }
    981 
    982       if (DriverEntry->Dependent) {
    983         if (SmmIsSchedulable (DriverEntry)) {
    984           SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);
    985           ReadyToRun = TRUE;
    986         }
    987       }
    988     }
    989   } while (ReadyToRun);
    990 
    991   //
    992   // If there is no more SMM driver to dispatch, stop the dispatch request
    993   //
    994   gRequestDispatch = FALSE;
    995   for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {
    996     DriverEntry = CR (Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE);
    997 
    998     if (!DriverEntry->Initialized){
    999       //
   1000       // We have SMM driver pending to dispatch
   1001       //
   1002       gRequestDispatch = TRUE;
   1003       break;
   1004     }
   1005   }
   1006 
   1007   gDispatcherRunning = FALSE;
   1008 
   1009   return EFI_SUCCESS;
   1010 }
   1011 
   1012 /**
   1013   Insert InsertedDriverEntry onto the mScheduledQueue. To do this you
   1014   must add any driver with a before dependency on InsertedDriverEntry first.
   1015   You do this by recursively calling this routine. After all the Befores are
   1016   processed you can add InsertedDriverEntry to the mScheduledQueue.
   1017   Then you can add any driver with an After dependency on InsertedDriverEntry
   1018   by recursively calling this routine.
   1019 
   1020   @param  InsertedDriverEntry   The driver to insert on the ScheduledLink Queue
   1021 
   1022 **/
   1023 VOID
   1024 SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (
   1025   IN  EFI_SMM_DRIVER_ENTRY   *InsertedDriverEntry
   1026   )
   1027 {
   1028   LIST_ENTRY            *Link;
   1029   EFI_SMM_DRIVER_ENTRY *DriverEntry;
   1030 
   1031   //
   1032   // Process Before Dependency
   1033   //
   1034   for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {
   1035     DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE);
   1036     if (DriverEntry->Before && DriverEntry->Dependent && DriverEntry != InsertedDriverEntry) {
   1037       DEBUG ((DEBUG_DISPATCH, "Evaluate SMM DEPEX for FFS(%g)\n", &DriverEntry->FileName));
   1038       DEBUG ((DEBUG_DISPATCH, "  BEFORE FFS(%g) = ", &DriverEntry->BeforeAfterGuid));
   1039       if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) {
   1040         //
   1041         // Recursively process BEFORE
   1042         //
   1043         DEBUG ((DEBUG_DISPATCH, "TRUE\n  END\n  RESULT = TRUE\n"));
   1044         SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);
   1045       } else {
   1046         DEBUG ((DEBUG_DISPATCH, "FALSE\n  END\n  RESULT = FALSE\n"));
   1047       }
   1048     }
   1049   }
   1050 
   1051   //
   1052   // Convert driver from Dependent to Scheduled state
   1053   //
   1054 
   1055   InsertedDriverEntry->Dependent = FALSE;
   1056   InsertedDriverEntry->Scheduled = TRUE;
   1057   InsertTailList (&mScheduledQueue, &InsertedDriverEntry->ScheduledLink);
   1058 
   1059 
   1060   //
   1061   // Process After Dependency
   1062   //
   1063   for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {
   1064     DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE);
   1065     if (DriverEntry->After && DriverEntry->Dependent && DriverEntry != InsertedDriverEntry) {
   1066       DEBUG ((DEBUG_DISPATCH, "Evaluate SMM DEPEX for FFS(%g)\n", &DriverEntry->FileName));
   1067       DEBUG ((DEBUG_DISPATCH, "  AFTER FFS(%g) = ", &DriverEntry->BeforeAfterGuid));
   1068       if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) {
   1069         //
   1070         // Recursively process AFTER
   1071         //
   1072         DEBUG ((DEBUG_DISPATCH, "TRUE\n  END\n  RESULT = TRUE\n"));
   1073         SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);
   1074       } else {
   1075         DEBUG ((DEBUG_DISPATCH, "FALSE\n  END\n  RESULT = FALSE\n"));
   1076       }
   1077     }
   1078   }
   1079 }
   1080 
   1081 /**
   1082   Return TRUE if the Fv has been processed, FALSE if not.
   1083 
   1084   @param  FvHandle              The handle of a FV that's being tested
   1085 
   1086   @retval TRUE                  Fv protocol on FvHandle has been processed
   1087   @retval FALSE                 Fv protocol on FvHandle has not yet been
   1088                                 processed
   1089 
   1090 **/
   1091 BOOLEAN
   1092 FvHasBeenProcessed (
   1093   IN EFI_HANDLE  FvHandle
   1094   )
   1095 {
   1096   LIST_ENTRY    *Link;
   1097   KNOWN_HANDLE  *KnownHandle;
   1098 
   1099   for (Link = mFvHandleList.ForwardLink; Link != &mFvHandleList; Link = Link->ForwardLink) {
   1100     KnownHandle = CR(Link, KNOWN_HANDLE, Link, KNOWN_HANDLE_SIGNATURE);
   1101     if (KnownHandle->Handle == FvHandle) {
   1102       return TRUE;
   1103     }
   1104   }
   1105   return FALSE;
   1106 }
   1107 
   1108 /**
   1109   Remember that Fv protocol on FvHandle has had it's drivers placed on the
   1110   mDiscoveredList. This fucntion adds entries on the mFvHandleList. Items are
   1111   never removed/freed from the mFvHandleList.
   1112 
   1113   @param  FvHandle              The handle of a FV that has been processed
   1114 
   1115 **/
   1116 VOID
   1117 FvIsBeingProcesssed (
   1118   IN EFI_HANDLE  FvHandle
   1119   )
   1120 {
   1121   KNOWN_HANDLE  *KnownHandle;
   1122 
   1123   KnownHandle = AllocatePool (sizeof (KNOWN_HANDLE));
   1124   ASSERT (KnownHandle != NULL);
   1125 
   1126   KnownHandle->Signature = KNOWN_HANDLE_SIGNATURE;
   1127   KnownHandle->Handle = FvHandle;
   1128   InsertTailList (&mFvHandleList, &KnownHandle->Link);
   1129 }
   1130 
   1131 /**
   1132   Convert FvHandle and DriverName into an EFI device path
   1133 
   1134   @param  Fv                    Fv protocol, needed to read Depex info out of
   1135                                 FLASH.
   1136   @param  FvHandle              Handle for Fv, needed in the
   1137                                 EFI_SMM_DRIVER_ENTRY so that the PE image can be
   1138                                 read out of the FV at a later time.
   1139   @param  DriverName            Name of driver to add to mDiscoveredList.
   1140 
   1141   @return Pointer to device path constructed from FvHandle and DriverName
   1142 
   1143 **/
   1144 EFI_DEVICE_PATH_PROTOCOL *
   1145 SmmFvToDevicePath (
   1146   IN  EFI_FIRMWARE_VOLUME2_PROTOCOL   *Fv,
   1147   IN  EFI_HANDLE                      FvHandle,
   1148   IN  EFI_GUID                        *DriverName
   1149   )
   1150 {
   1151   EFI_STATUS                          Status;
   1152   EFI_DEVICE_PATH_PROTOCOL            *FvDevicePath;
   1153   EFI_DEVICE_PATH_PROTOCOL            *FileNameDevicePath;
   1154 
   1155   //
   1156   // Remember the device path of the FV
   1157   //
   1158   Status = gBS->HandleProtocol (FvHandle, &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath);
   1159   if (EFI_ERROR (Status)) {
   1160     FileNameDevicePath = NULL;
   1161   } else {
   1162     //
   1163     // Build a device path to the file in the FV to pass into gBS->LoadImage
   1164     //
   1165     EfiInitializeFwVolDevicepathNode (&mFvDevicePath.File, DriverName);
   1166     SetDevicePathEndNode (&mFvDevicePath.End);
   1167 
   1168     //
   1169     // Note: FileNameDevicePath is in DXE memory
   1170     //
   1171     FileNameDevicePath = AppendDevicePath (
   1172                             FvDevicePath,
   1173                             (EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath
   1174                             );
   1175   }
   1176   return FileNameDevicePath;
   1177 }
   1178 
   1179 /**
   1180   Add an entry to the mDiscoveredList. Allocate memory to store the DriverEntry,
   1181   and initilize any state variables. Read the Depex from the FV and store it
   1182   in DriverEntry. Pre-process the Depex to set the Before and After state.
   1183   The Discovered list is never free'ed and contains booleans that represent the
   1184   other possible SMM driver states.
   1185 
   1186   @param  Fv                    Fv protocol, needed to read Depex info out of
   1187                                 FLASH.
   1188   @param  FvHandle              Handle for Fv, needed in the
   1189                                 EFI_SMM_DRIVER_ENTRY so that the PE image can be
   1190                                 read out of the FV at a later time.
   1191   @param  DriverName            Name of driver to add to mDiscoveredList.
   1192 
   1193   @retval EFI_SUCCESS           If driver was added to the mDiscoveredList.
   1194   @retval EFI_ALREADY_STARTED   The driver has already been started. Only one
   1195                                 DriverName may be active in the system at any one
   1196                                 time.
   1197 
   1198 **/
   1199 EFI_STATUS
   1200 SmmAddToDriverList (
   1201   IN EFI_FIRMWARE_VOLUME2_PROTOCOL  *Fv,
   1202   IN EFI_HANDLE                     FvHandle,
   1203   IN EFI_GUID                       *DriverName
   1204   )
   1205 {
   1206   EFI_SMM_DRIVER_ENTRY  *DriverEntry;
   1207 
   1208   //
   1209   // Create the Driver Entry for the list. ZeroPool initializes lots of variables to
   1210   // NULL or FALSE.
   1211   //
   1212   DriverEntry = AllocateZeroPool (sizeof (EFI_SMM_DRIVER_ENTRY));
   1213   ASSERT (DriverEntry != NULL);
   1214 
   1215   DriverEntry->Signature        = EFI_SMM_DRIVER_ENTRY_SIGNATURE;
   1216   CopyGuid (&DriverEntry->FileName, DriverName);
   1217   DriverEntry->FvHandle         = FvHandle;
   1218   DriverEntry->Fv               = Fv;
   1219   DriverEntry->FvFileDevicePath = SmmFvToDevicePath (Fv, FvHandle, DriverName);
   1220 
   1221   SmmGetDepexSectionAndPreProccess (DriverEntry);
   1222 
   1223   InsertTailList (&mDiscoveredList, &DriverEntry->Link);
   1224   gRequestDispatch = TRUE;
   1225 
   1226   return EFI_SUCCESS;
   1227 }
   1228 
   1229 /**
   1230   This function is the main entry point for an SMM handler dispatch
   1231   or communicate-based callback.
   1232 
   1233   Event notification that is fired every time a FV dispatch protocol is added.
   1234   More than one protocol may have been added when this event is fired, so you
   1235   must loop on SmmLocateHandle () to see how many protocols were added and
   1236   do the following to each FV:
   1237   If the Fv has already been processed, skip it. If the Fv has not been
   1238   processed then mark it as being processed, as we are about to process it.
   1239   Read the Fv and add any driver in the Fv to the mDiscoveredList.The
   1240   mDiscoveredList is never free'ed and contains variables that define
   1241   the other states the SMM driver transitions to..
   1242   While you are at it read the A Priori file into memory.
   1243   Place drivers in the A Priori list onto the mScheduledQueue.
   1244 
   1245   @param  DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
   1246   @param  Context         Points to an optional handler context which was specified when the handler was registered.
   1247   @param  CommBuffer      A pointer to a collection of data in memory that will
   1248                           be conveyed from a non-SMM environment into an SMM environment.
   1249   @param  CommBufferSize  The size of the CommBuffer.
   1250 
   1251   @return Status Code
   1252 
   1253 **/
   1254 EFI_STATUS
   1255 EFIAPI
   1256 SmmDriverDispatchHandler (
   1257   IN     EFI_HANDLE  DispatchHandle,
   1258   IN     CONST VOID  *Context,        OPTIONAL
   1259   IN OUT VOID        *CommBuffer,     OPTIONAL
   1260   IN OUT UINTN       *CommBufferSize  OPTIONAL
   1261   )
   1262 {
   1263   EFI_STATUS                    Status;
   1264   UINTN                         HandleCount;
   1265   EFI_HANDLE                    *HandleBuffer;
   1266   EFI_STATUS                    GetNextFileStatus;
   1267   EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
   1268   EFI_DEVICE_PATH_PROTOCOL      *FvDevicePath;
   1269   EFI_HANDLE                    FvHandle;
   1270   EFI_GUID                      NameGuid;
   1271   UINTN                         Key;
   1272   EFI_FV_FILETYPE               Type;
   1273   EFI_FV_FILE_ATTRIBUTES        Attributes;
   1274   UINTN                         Size;
   1275   EFI_SMM_DRIVER_ENTRY          *DriverEntry;
   1276   EFI_GUID                      *AprioriFile;
   1277   UINTN                         AprioriEntryCount;
   1278   UINTN                         HandleIndex;
   1279   UINTN                         SmmTypeIndex;
   1280   UINTN                         AprioriIndex;
   1281   LIST_ENTRY                    *Link;
   1282   UINT32                        AuthenticationStatus;
   1283   UINTN                         SizeOfBuffer;
   1284 
   1285   HandleBuffer = NULL;
   1286   Status = gBS->LocateHandleBuffer (
   1287                   ByProtocol,
   1288                   &gEfiFirmwareVolume2ProtocolGuid,
   1289                   NULL,
   1290                   &HandleCount,
   1291                   &HandleBuffer
   1292                   );
   1293   if (EFI_ERROR (Status)) {
   1294     return EFI_NOT_FOUND;
   1295   }
   1296 
   1297   for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
   1298     FvHandle = HandleBuffer[HandleIndex];
   1299 
   1300     if (FvHasBeenProcessed (FvHandle)) {
   1301       //
   1302       // This Fv has already been processed so lets skip it!
   1303       //
   1304       continue;
   1305     }
   1306 
   1307     //
   1308     // Since we are about to process this Fv mark it as processed.
   1309     //
   1310     FvIsBeingProcesssed (FvHandle);
   1311 
   1312     Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv);
   1313     if (EFI_ERROR (Status)) {
   1314       //
   1315       // FvHandle must have a Firmware Volume2 Protocol thus we should never get here.
   1316       //
   1317       ASSERT (FALSE);
   1318       continue;
   1319     }
   1320 
   1321     Status = gBS->HandleProtocol (FvHandle, &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath);
   1322     if (EFI_ERROR (Status)) {
   1323       //
   1324       // The Firmware volume doesn't have device path, can't be dispatched.
   1325       //
   1326       continue;
   1327     }
   1328 
   1329     //
   1330     // Discover Drivers in FV and add them to the Discovered Driver List.
   1331     // Process EFI_FV_FILETYPE_SMM type and then EFI_FV_FILETYPE_COMBINED_SMM_DXE
   1332     //  EFI_FV_FILETYPE_SMM_CORE is processed to produce a Loaded Image protocol for the core
   1333     //
   1334     for (SmmTypeIndex = 0; SmmTypeIndex < sizeof (mSmmFileTypes)/sizeof (EFI_FV_FILETYPE); SmmTypeIndex++) {
   1335       //
   1336       // Initialize the search key
   1337       //
   1338       Key = 0;
   1339       do {
   1340         Type = mSmmFileTypes[SmmTypeIndex];
   1341         GetNextFileStatus = Fv->GetNextFile (
   1342                                   Fv,
   1343                                   &Key,
   1344                                   &Type,
   1345                                   &NameGuid,
   1346                                   &Attributes,
   1347                                   &Size
   1348                                   );
   1349         if (!EFI_ERROR (GetNextFileStatus)) {
   1350           if (Type == EFI_FV_FILETYPE_SMM_CORE) {
   1351             //
   1352             // If this is the SMM core fill in it's DevicePath & DeviceHandle
   1353             //
   1354             if (mSmmCoreLoadedImage->FilePath == NULL) {
   1355               //
   1356               // Maybe one special FV contains only one SMM_CORE module, so its device path must
   1357               // be initialized completely.
   1358               //
   1359               EfiInitializeFwVolDevicepathNode (&mFvDevicePath.File, &NameGuid);
   1360               SetDevicePathEndNode (&mFvDevicePath.End);
   1361 
   1362               //
   1363               // Make an EfiBootServicesData buffer copy of FilePath
   1364               //
   1365               Status = gBS->AllocatePool (
   1366                               EfiBootServicesData,
   1367                               GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath),
   1368                               (VOID **)&mSmmCoreLoadedImage->FilePath
   1369                               );
   1370               ASSERT_EFI_ERROR (Status);
   1371               CopyMem (mSmmCoreLoadedImage->FilePath, &mFvDevicePath, GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath));
   1372 
   1373               mSmmCoreLoadedImage->DeviceHandle = FvHandle;
   1374             }
   1375             if (mSmmCoreDriverEntry->SmmLoadedImage.FilePath == NULL) {
   1376               //
   1377               // Maybe one special FV contains only one SMM_CORE module, so its device path must
   1378               // be initialized completely.
   1379               //
   1380               EfiInitializeFwVolDevicepathNode (&mFvDevicePath.File, &NameGuid);
   1381               SetDevicePathEndNode (&mFvDevicePath.End);
   1382 
   1383               //
   1384               // Make a buffer copy FilePath
   1385               //
   1386               Status = SmmAllocatePool (
   1387                          EfiRuntimeServicesData,
   1388                          GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath),
   1389                          (VOID **)&mSmmCoreDriverEntry->SmmLoadedImage.FilePath
   1390                          );
   1391               ASSERT_EFI_ERROR (Status);
   1392               CopyMem (mSmmCoreDriverEntry->SmmLoadedImage.FilePath, &mFvDevicePath, GetDevicePathSize((EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath));
   1393 
   1394               mSmmCoreDriverEntry->SmmLoadedImage.DeviceHandle = FvHandle;
   1395             }
   1396           } else {
   1397             SmmAddToDriverList (Fv, FvHandle, &NameGuid);
   1398           }
   1399         }
   1400       } while (!EFI_ERROR (GetNextFileStatus));
   1401     }
   1402 
   1403     //
   1404     // Read the array of GUIDs from the Apriori file if it is present in the firmware volume
   1405     // (Note: AprioriFile is in DXE memory)
   1406     //
   1407     AprioriFile = NULL;
   1408     Status = Fv->ReadSection (
   1409                   Fv,
   1410                   &gAprioriGuid,
   1411                   EFI_SECTION_RAW,
   1412                   0,
   1413                   (VOID **)&AprioriFile,
   1414                   &SizeOfBuffer,
   1415                   &AuthenticationStatus
   1416                   );
   1417     if (!EFI_ERROR (Status)) {
   1418       AprioriEntryCount = SizeOfBuffer / sizeof (EFI_GUID);
   1419     } else {
   1420       AprioriEntryCount = 0;
   1421     }
   1422 
   1423     //
   1424     // Put drivers on Apriori List on the Scheduled queue. The Discovered List includes
   1425     // drivers not in the current FV and these must be skipped since the a priori list
   1426     // is only valid for the FV that it resided in.
   1427     //
   1428 
   1429     for (AprioriIndex = 0; AprioriIndex < AprioriEntryCount; AprioriIndex++) {
   1430       for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {
   1431         DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE);
   1432         if (CompareGuid (&DriverEntry->FileName, &AprioriFile[AprioriIndex]) &&
   1433             (FvHandle == DriverEntry->FvHandle)) {
   1434           DriverEntry->Dependent = FALSE;
   1435           DriverEntry->Scheduled = TRUE;
   1436           InsertTailList (&mScheduledQueue, &DriverEntry->ScheduledLink);
   1437           DEBUG ((DEBUG_DISPATCH, "Evaluate SMM DEPEX for FFS(%g)\n", &DriverEntry->FileName));
   1438           DEBUG ((DEBUG_DISPATCH, "  RESULT = TRUE (Apriori)\n"));
   1439           break;
   1440         }
   1441       }
   1442     }
   1443 
   1444     //
   1445     // Free data allocated by Fv->ReadSection ()
   1446     //
   1447     // The UEFI Boot Services FreePool() function must be used because Fv->ReadSection
   1448     // used the UEFI Boot Services AllocatePool() function
   1449     //
   1450     gBS->FreePool (AprioriFile);
   1451   }
   1452 
   1453   //
   1454   // Execute the SMM Dispatcher on any newly discovered FVs and previously
   1455   // discovered SMM drivers that have been discovered but not dispatched.
   1456   //
   1457   Status = SmmDispatcher ();
   1458 
   1459   //
   1460   // Check to see if CommBuffer and CommBufferSize are valid
   1461   //
   1462   if (CommBuffer != NULL && CommBufferSize != NULL) {
   1463     if (*CommBufferSize > 0) {
   1464       if (Status == EFI_NOT_READY) {
   1465         //
   1466         // If a the SMM Core Entry Point was just registered, then set flag to
   1467         // request the SMM Dispatcher to be restarted.
   1468         //
   1469         *(UINT8 *)CommBuffer = COMM_BUFFER_SMM_DISPATCH_RESTART;
   1470       } else if (!EFI_ERROR (Status)) {
   1471         //
   1472         // Set the flag to show that the SMM Dispatcher executed without errors
   1473         //
   1474         *(UINT8 *)CommBuffer = COMM_BUFFER_SMM_DISPATCH_SUCCESS;
   1475       } else {
   1476         //
   1477         // Set the flag to show that the SMM Dispatcher encountered an error
   1478         //
   1479         *(UINT8 *)CommBuffer = COMM_BUFFER_SMM_DISPATCH_ERROR;
   1480       }
   1481     }
   1482   }
   1483 
   1484   return EFI_SUCCESS;
   1485 }
   1486 
   1487 /**
   1488   Traverse the discovered list for any drivers that were discovered but not loaded
   1489   because the dependency experessions evaluated to false.
   1490 
   1491 **/
   1492 VOID
   1493 SmmDisplayDiscoveredNotDispatched (
   1494   VOID
   1495   )
   1496 {
   1497   LIST_ENTRY                   *Link;
   1498   EFI_SMM_DRIVER_ENTRY         *DriverEntry;
   1499 
   1500   for (Link = mDiscoveredList.ForwardLink;Link !=&mDiscoveredList; Link = Link->ForwardLink) {
   1501     DriverEntry = CR(Link, EFI_SMM_DRIVER_ENTRY, Link, EFI_SMM_DRIVER_ENTRY_SIGNATURE);
   1502     if (DriverEntry->Dependent) {
   1503       DEBUG ((DEBUG_LOAD, "SMM Driver %g was discovered but not loaded!!\n", &DriverEntry->FileName));
   1504     }
   1505   }
   1506 }
   1507