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
CheckAndMarkFixLoadingMemoryUsageBitMap(IN EFI_PHYSICAL_ADDRESS ImageBase,IN UINTN ImageSize)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
GetPeCoffImageFixLoadingAssignedAddress(IN OUT PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext)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
SmmLoadImage(IN OUT EFI_SMM_DRIVER_ENTRY * DriverEntry)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
SmmPreProcessDepex(IN EFI_SMM_DRIVER_ENTRY * DriverEntry)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
SmmGetDepexSectionAndPreProccess(IN EFI_SMM_DRIVER_ENTRY * DriverEntry)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
SmmDispatcher(VOID)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
SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter(IN EFI_SMM_DRIVER_ENTRY * InsertedDriverEntry)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
FvHasBeenProcessed(IN EFI_HANDLE FvHandle)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
FvIsBeingProcesssed(IN EFI_HANDLE FvHandle)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 *
SmmFvToDevicePath(IN EFI_FIRMWARE_VOLUME2_PROTOCOL * Fv,IN EFI_HANDLE FvHandle,IN EFI_GUID * DriverName)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
SmmAddToDriverList(IN EFI_FIRMWARE_VOLUME2_PROTOCOL * Fv,IN EFI_HANDLE FvHandle,IN EFI_GUID * DriverName)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
SmmDriverDispatchHandler(IN EFI_HANDLE DispatchHandle,IN CONST VOID * Context,OPTIONAL IN OUT VOID * CommBuffer,OPTIONAL IN OUT UINTN * CommBufferSize OPTIONAL)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
SmmDisplayDiscoveredNotDispatched(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