1 /** @file
2   MM 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 - 2014, Intel Corporation. All rights reserved.<BR>
32   Copyright (c) 2016 - 2018, ARM Limited. All rights reserved.<BR>
33 
34   SPDX-License-Identifier: BSD-2-Clause-Patent
35 
36 **/
37 
38 #include "StandaloneMmCore.h"
39 
40 //
41 // MM Dispatcher Data structures
42 //
43 #define KNOWN_HANDLE_SIGNATURE  SIGNATURE_32('k','n','o','w')
44 
45 typedef struct {
46   UINTN           Signature;
47   LIST_ENTRY      Link;         // mFvHandleList
48   EFI_HANDLE      Handle;
49 } KNOWN_HANDLE;
50 
51 //
52 // Function Prototypes
53 //
54 
55 EFI_STATUS
56 MmCoreFfsFindMmDriver (
57   IN  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader
58   );
59 
60 /**
61   Insert InsertedDriverEntry onto the mScheduledQueue. To do this you
62   must add any driver with a before dependency on InsertedDriverEntry first.
63   You do this by recursively calling this routine. After all the Befores are
64   processed you can add InsertedDriverEntry to the mScheduledQueue.
65   Then you can add any driver with an After dependency on InsertedDriverEntry
66   by recursively calling this routine.
67 
68   @param  InsertedDriverEntry   The driver to insert on the ScheduledLink Queue
69 
70 **/
71 VOID
72 MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (
73   IN  EFI_MM_DRIVER_ENTRY   *InsertedDriverEntry
74   );
75 
76 //
77 // The Driver List contains one copy of every driver that has been discovered.
78 // Items are never removed from the driver list. List of EFI_MM_DRIVER_ENTRY
79 //
80 LIST_ENTRY  mDiscoveredList = INITIALIZE_LIST_HEAD_VARIABLE (mDiscoveredList);
81 
82 //
83 // Queue of drivers that are ready to dispatch. This queue is a subset of the
84 // mDiscoveredList.list of EFI_MM_DRIVER_ENTRY.
85 //
86 LIST_ENTRY  mScheduledQueue = INITIALIZE_LIST_HEAD_VARIABLE (mScheduledQueue);
87 
88 //
89 // List of handles who's Fv's have been parsed and added to the mFwDriverList.
90 //
91 LIST_ENTRY  mFvHandleList = INITIALIZE_LIST_HEAD_VARIABLE (mFvHandleList);
92 
93 //
94 // Flag for the MM Dispacher.  TRUE if dispatcher is execuing.
95 //
96 BOOLEAN  gDispatcherRunning = FALSE;
97 
98 //
99 // Flag for the MM Dispacher.  TRUE if there is one or more MM drivers ready to be dispatched
100 //
101 BOOLEAN  gRequestDispatch = FALSE;
102 
103 //
104 // The global variable is defined for Loading modules at fixed address feature to track the MM code
105 // memory range usage. It is a bit mapped array in which every bit indicates the correspoding
106 // memory page available or not.
107 //
108 GLOBAL_REMOVE_IF_UNREFERENCED    UINT64                *mMmCodeMemoryRangeUsageBitMap=NULL;
109 
110 /**
111   To check memory usage bit map array to figure out if the memory range in which the image will be loaded
112   is available or not. If memory range is avaliable, the function will mark the correponding bits to 1
113   which indicates the memory range is used. The function is only invoked when load modules at fixed address
114   feature is enabled.
115 
116   @param  ImageBase                The base addres the image will be loaded at.
117   @param  ImageSize                The size of the image
118 
119   @retval EFI_SUCCESS              The memory range the image will be loaded in is available
120   @retval EFI_NOT_FOUND            The memory range the image will be loaded in is not available
121 **/
122 EFI_STATUS
CheckAndMarkFixLoadingMemoryUsageBitMap(IN EFI_PHYSICAL_ADDRESS ImageBase,IN UINTN ImageSize)123 CheckAndMarkFixLoadingMemoryUsageBitMap (
124   IN  EFI_PHYSICAL_ADDRESS          ImageBase,
125   IN  UINTN                         ImageSize
126   )
127 {
128   UINT32                             MmCodePageNumber;
129   UINT64                             MmCodeSize;
130   EFI_PHYSICAL_ADDRESS               MmCodeBase;
131   UINTN                              BaseOffsetPageNumber;
132   UINTN                              TopOffsetPageNumber;
133   UINTN                              Index;
134 
135   //
136   // Build tool will calculate the smm code size and then patch the PcdLoadFixAddressMmCodePageNumber
137   //
138   MmCodePageNumber = 0;
139   MmCodeSize = EFI_PAGES_TO_SIZE (MmCodePageNumber);
140   MmCodeBase = gLoadModuleAtFixAddressMmramBase;
141 
142   //
143   // If the memory usage bit map is not initialized,  do it. Every bit in the array
144   // indicate the status of the corresponding memory page, available or not
145   //
146   if (mMmCodeMemoryRangeUsageBitMap == NULL) {
147     mMmCodeMemoryRangeUsageBitMap = AllocateZeroPool (((MmCodePageNumber / 64) + 1) * sizeof (UINT64));
148   }
149 
150   //
151   // If the Dxe code memory range is not allocated or the bit map array allocation failed, return EFI_NOT_FOUND
152   //
153   if (mMmCodeMemoryRangeUsageBitMap == NULL) {
154     return EFI_NOT_FOUND;
155   }
156 
157   //
158   // see if the memory range for loading the image is in the MM code range.
159   //
160   if (MmCodeBase + MmCodeSize <  ImageBase + ImageSize || MmCodeBase >  ImageBase) {
161     return EFI_NOT_FOUND;
162   }
163 
164   //
165   // Test if the memory is avalaible or not.
166   //
167   BaseOffsetPageNumber = (UINTN)EFI_SIZE_TO_PAGES ((UINT32)(ImageBase - MmCodeBase));
168   TopOffsetPageNumber  = (UINTN)EFI_SIZE_TO_PAGES ((UINT32)(ImageBase + ImageSize - MmCodeBase));
169   for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index ++) {
170     if ((mMmCodeMemoryRangeUsageBitMap[Index / 64] & LShiftU64 (1, (Index % 64))) != 0) {
171       //
172       // This page is already used.
173       //
174       return EFI_NOT_FOUND;
175     }
176   }
177 
178   //
179   // Being here means the memory range is available.  So mark the bits for the memory range
180   //
181   for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index ++) {
182     mMmCodeMemoryRangeUsageBitMap[Index / 64] |= LShiftU64 (1, (Index % 64));
183   }
184   return  EFI_SUCCESS;
185 }
186 
187 /**
188   Get the fixed loading address from image header assigned by build tool. This function only be called
189   when Loading module at Fixed address feature enabled.
190 
191   @param  ImageContext              Pointer to the image context structure that describes the PE/COFF
192                                     image that needs to be examined by this function.
193   @retval EFI_SUCCESS               An fixed loading address is assigned to this image by build tools .
194   @retval EFI_NOT_FOUND             The image has no assigned fixed loadding address.
195 
196 **/
197 EFI_STATUS
GetPeCoffImageFixLoadingAssignedAddress(IN OUT PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext)198 GetPeCoffImageFixLoadingAssignedAddress(
199   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
200   )
201 {
202   UINTN                              SectionHeaderOffset;
203   EFI_STATUS                         Status;
204   EFI_IMAGE_SECTION_HEADER           SectionHeader;
205   EFI_IMAGE_OPTIONAL_HEADER_UNION    *ImgHdr;
206   EFI_PHYSICAL_ADDRESS               FixLoadingAddress;
207   UINT16                             Index;
208   UINTN                              Size;
209   UINT16                             NumberOfSections;
210   UINT64                             ValueInSectionHeader;
211 
212   FixLoadingAddress = 0;
213   Status = EFI_NOT_FOUND;
214 
215   //
216   // Get PeHeader pointer
217   //
218   ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((CHAR8* )ImageContext->Handle + ImageContext->PeCoffHeaderOffset);
219   SectionHeaderOffset = ImageContext->PeCoffHeaderOffset + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) +
220     ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader;
221   NumberOfSections = ImgHdr->Pe32.FileHeader.NumberOfSections;
222 
223   //
224   // Get base address from the first section header that doesn't point to code section.
225   //
226   for (Index = 0; Index < NumberOfSections; Index++) {
227     //
228     // Read section header from file
229     //
230     Size = sizeof (EFI_IMAGE_SECTION_HEADER);
231     Status = ImageContext->ImageRead (
232                              ImageContext->Handle,
233                              SectionHeaderOffset,
234                              &Size,
235                              &SectionHeader
236                              );
237     if (EFI_ERROR (Status)) {
238       return Status;
239     }
240 
241     Status = EFI_NOT_FOUND;
242 
243     if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_CNT_CODE) == 0) {
244       //
245       // Build tool will save the address in PointerToRelocations & PointerToLineNumbers fields
246       // in the first section header that doesn't point to code section in image header. So there
247       // is an assumption that when the feature is enabled, if a module with a loading address
248       // assigned by tools, the PointerToRelocations & PointerToLineNumbers fields should not be
249       // Zero, or else, these 2 fields should be set to Zero
250       //
251       ValueInSectionHeader = ReadUnaligned64 ((UINT64*)&SectionHeader.PointerToRelocations);
252       if (ValueInSectionHeader != 0) {
253         //
254         // Found first section header that doesn't point to code section in which build tool saves the
255         // offset to SMRAM base as image base in PointerToRelocations & PointerToLineNumbers fields
256         //
257         FixLoadingAddress = (EFI_PHYSICAL_ADDRESS)(gLoadModuleAtFixAddressMmramBase + (INT64)ValueInSectionHeader);
258         //
259         // Check if the memory range is available.
260         //
261         Status = CheckAndMarkFixLoadingMemoryUsageBitMap (FixLoadingAddress, (UINTN)(ImageContext->ImageSize + ImageContext->SectionAlignment));
262         if (!EFI_ERROR(Status)) {
263           //
264           // The assigned address is valid. Return the specified loading address
265           //
266           ImageContext->ImageAddress = FixLoadingAddress;
267         }
268       }
269       break;
270     }
271     SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
272   }
273   DEBUG ((DEBUG_INFO|DEBUG_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address %x, Status = %r\n",
274           FixLoadingAddress, Status));
275   return Status;
276 }
277 /**
278   Loads an EFI image into SMRAM.
279 
280   @param  DriverEntry             EFI_MM_DRIVER_ENTRY instance
281 
282   @return EFI_STATUS
283 
284 **/
285 EFI_STATUS
286 EFIAPI
MmLoadImage(IN OUT EFI_MM_DRIVER_ENTRY * DriverEntry)287 MmLoadImage (
288   IN OUT EFI_MM_DRIVER_ENTRY  *DriverEntry
289   )
290 {
291   UINTN                          PageCount;
292   EFI_STATUS                     Status;
293   EFI_PHYSICAL_ADDRESS           DstBuffer;
294   PE_COFF_LOADER_IMAGE_CONTEXT   ImageContext;
295 
296   DEBUG ((DEBUG_INFO, "MmLoadImage - %g\n", &DriverEntry->FileName));
297 
298   Status               = EFI_SUCCESS;
299 
300   //
301   // Initialize ImageContext
302   //
303   ImageContext.Handle = DriverEntry->Pe32Data;
304   ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
305 
306   //
307   // Get information about the image being loaded
308   //
309   Status = PeCoffLoaderGetImageInfo (&ImageContext);
310   if (EFI_ERROR (Status)) {
311     return Status;
312   }
313 
314   PageCount = (UINTN)EFI_SIZE_TO_PAGES ((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment);
315   DstBuffer = (UINTN)(-1);
316 
317   Status = MmAllocatePages (
318              AllocateMaxAddress,
319              EfiRuntimeServicesCode,
320              PageCount,
321              &DstBuffer
322              );
323   if (EFI_ERROR (Status)) {
324     return Status;
325   }
326 
327   ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer;
328 
329   //
330   // Align buffer on section boundry
331   //
332   ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;
333   ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext.SectionAlignment - 1));
334 
335   //
336   // Load the image to our new buffer
337   //
338   Status = PeCoffLoaderLoadImage (&ImageContext);
339   if (EFI_ERROR (Status)) {
340     MmFreePages (DstBuffer, PageCount);
341     return Status;
342   }
343 
344   //
345   // Relocate the image in our new buffer
346   //
347   Status = PeCoffLoaderRelocateImage (&ImageContext);
348   if (EFI_ERROR (Status)) {
349     MmFreePages (DstBuffer, PageCount);
350     return Status;
351   }
352 
353   //
354   // Flush the instruction cache so the image data are written before we execute it
355   //
356   InvalidateInstructionCacheRange ((VOID *)(UINTN) ImageContext.ImageAddress, (UINTN) ImageContext.ImageSize);
357 
358   //
359   // Save Image EntryPoint in DriverEntry
360   //
361   DriverEntry->ImageEntryPoint  = ImageContext.EntryPoint;
362   DriverEntry->ImageBuffer      = DstBuffer;
363   DriverEntry->NumberOfPage     = PageCount;
364 
365   if (mEfiSystemTable != NULL) {
366     Status = mEfiSystemTable->BootServices->AllocatePool (
367                                               EfiBootServicesData,
368                                               sizeof (EFI_LOADED_IMAGE_PROTOCOL),
369                                               (VOID **)&DriverEntry->LoadedImage
370                                               );
371     if (EFI_ERROR (Status)) {
372       MmFreePages (DstBuffer, PageCount);
373       return Status;
374     }
375 
376     ZeroMem (DriverEntry->LoadedImage, sizeof (EFI_LOADED_IMAGE_PROTOCOL));
377     //
378     // Fill in the remaining fields of the Loaded Image Protocol instance.
379     // Note: ImageBase is an SMRAM address that can not be accessed outside of SMRAM if SMRAM window is closed.
380     //
381     DriverEntry->LoadedImage->Revision      = EFI_LOADED_IMAGE_PROTOCOL_REVISION;
382     DriverEntry->LoadedImage->ParentHandle  = NULL;
383     DriverEntry->LoadedImage->SystemTable   = mEfiSystemTable;
384     DriverEntry->LoadedImage->DeviceHandle  = NULL;
385     DriverEntry->LoadedImage->FilePath      = NULL;
386 
387     DriverEntry->LoadedImage->ImageBase     = (VOID *)(UINTN)DriverEntry->ImageBuffer;
388     DriverEntry->LoadedImage->ImageSize     = ImageContext.ImageSize;
389     DriverEntry->LoadedImage->ImageCodeType = EfiRuntimeServicesCode;
390     DriverEntry->LoadedImage->ImageDataType = EfiRuntimeServicesData;
391 
392     //
393     // Create a new image handle in the UEFI handle database for the MM Driver
394     //
395     DriverEntry->ImageHandle = NULL;
396     Status = mEfiSystemTable->BootServices->InstallMultipleProtocolInterfaces (
397                                               &DriverEntry->ImageHandle,
398                                               &gEfiLoadedImageProtocolGuid,
399                                               DriverEntry->LoadedImage,
400                                               NULL
401                                               );
402   }
403 
404   //
405   // Print the load address and the PDB file name if it is available
406   //
407   DEBUG_CODE_BEGIN ();
408 
409   UINTN Index;
410   UINTN StartIndex;
411   CHAR8 EfiFileName[256];
412 
413   DEBUG ((DEBUG_INFO | DEBUG_LOAD,
414           "Loading MM driver at 0x%11p EntryPoint=0x%11p ",
415           (VOID *)(UINTN) ImageContext.ImageAddress,
416           FUNCTION_ENTRY_POINT (ImageContext.EntryPoint)));
417 
418   //
419   // Print Module Name by Pdb file path.
420   // Windows and Unix style file path are all trimmed correctly.
421   //
422   if (ImageContext.PdbPointer != NULL) {
423     StartIndex = 0;
424     for (Index = 0; ImageContext.PdbPointer[Index] != 0; Index++) {
425       if ((ImageContext.PdbPointer[Index] == '\\') || (ImageContext.PdbPointer[Index] == '/')) {
426         StartIndex = Index + 1;
427       }
428     }
429 
430     //
431     // Copy the PDB file name to our temporary string, and replace .pdb with .efi
432     // The PDB file name is limited in the range of 0~255.
433     // If the length is bigger than 255, trim the redudant characters to avoid overflow in array boundary.
434     //
435     for (Index = 0; Index < sizeof (EfiFileName) - 4; Index++) {
436       EfiFileName[Index] = ImageContext.PdbPointer[Index + StartIndex];
437       if (EfiFileName[Index] == 0) {
438         EfiFileName[Index] = '.';
439       }
440       if (EfiFileName[Index] == '.') {
441         EfiFileName[Index + 1] = 'e';
442         EfiFileName[Index + 2] = 'f';
443         EfiFileName[Index + 3] = 'i';
444         EfiFileName[Index + 4] = 0;
445         break;
446       }
447     }
448 
449     if (Index == sizeof (EfiFileName) - 4) {
450       EfiFileName[Index] = 0;
451     }
452     DEBUG ((DEBUG_INFO | DEBUG_LOAD, "%a", EfiFileName));
453   }
454   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "\n"));
455 
456   DEBUG_CODE_END ();
457 
458   return Status;
459 }
460 
461 /**
462   Preprocess dependency expression and update DriverEntry to reflect the
463   state of  Before and After dependencies. If DriverEntry->Before
464   or DriverEntry->After is set it will never be cleared.
465 
466   @param  DriverEntry           DriverEntry element to update .
467 
468   @retval EFI_SUCCESS           It always works.
469 
470 **/
471 EFI_STATUS
MmPreProcessDepex(IN EFI_MM_DRIVER_ENTRY * DriverEntry)472 MmPreProcessDepex (
473   IN EFI_MM_DRIVER_ENTRY  *DriverEntry
474   )
475 {
476   UINT8  *Iterator;
477 
478   Iterator = DriverEntry->Depex;
479   DriverEntry->Dependent = TRUE;
480 
481   if (*Iterator == EFI_DEP_BEFORE) {
482     DriverEntry->Before = TRUE;
483   } else if (*Iterator == EFI_DEP_AFTER) {
484     DriverEntry->After = TRUE;
485   }
486 
487   if (DriverEntry->Before || DriverEntry->After) {
488     CopyMem (&DriverEntry->BeforeAfterGuid, Iterator + 1, sizeof (EFI_GUID));
489   }
490 
491   return EFI_SUCCESS;
492 }
493 
494 /**
495   Read Depex and pre-process the Depex for Before and After. If Section Extraction
496   protocol returns an error via ReadSection defer the reading of the Depex.
497 
498   @param  DriverEntry           Driver to work on.
499 
500   @retval EFI_SUCCESS           Depex read and preprossesed
501   @retval EFI_PROTOCOL_ERROR    The section extraction protocol returned an error
502                                 and  Depex reading needs to be retried.
503   @retval Error                 DEPEX not found.
504 
505 **/
506 EFI_STATUS
MmGetDepexSectionAndPreProccess(IN EFI_MM_DRIVER_ENTRY * DriverEntry)507 MmGetDepexSectionAndPreProccess (
508   IN EFI_MM_DRIVER_ENTRY  *DriverEntry
509   )
510 {
511   EFI_STATUS                     Status;
512 
513   //
514   // Data already read
515   //
516   if (DriverEntry->Depex == NULL) {
517     Status = EFI_NOT_FOUND;
518   } else {
519     Status = EFI_SUCCESS;
520   }
521   if (EFI_ERROR (Status)) {
522     if (Status == EFI_PROTOCOL_ERROR) {
523       //
524       // The section extraction protocol failed so set protocol error flag
525       //
526       DriverEntry->DepexProtocolError = TRUE;
527     } else {
528       //
529       // If no Depex assume depend on all architectural protocols
530       //
531       DriverEntry->Depex = NULL;
532       DriverEntry->Dependent = TRUE;
533       DriverEntry->DepexProtocolError = FALSE;
534     }
535   } else {
536     //
537     // Set Before and After state information based on Depex
538     // Driver will be put in Dependent state
539     //
540     MmPreProcessDepex (DriverEntry);
541     DriverEntry->DepexProtocolError = FALSE;
542   }
543 
544   return Status;
545 }
546 
547 /**
548   This is the main Dispatcher for MM and it exits when there are no more
549   drivers to run. Drain the mScheduledQueue and load and start a PE
550   image for each driver. Search the mDiscoveredList to see if any driver can
551   be placed on the mScheduledQueue. If no drivers are placed on the
552   mScheduledQueue exit the function.
553 
554   @retval EFI_SUCCESS           All of the MM Drivers that could be dispatched
555                                 have been run and the MM Entry Point has been
556                                 registered.
557   @retval EFI_NOT_READY         The MM Driver that registered the MM Entry Point
558                                 was just dispatched.
559   @retval EFI_NOT_FOUND         There are no MM Drivers available to be dispatched.
560   @retval EFI_ALREADY_STARTED   The MM Dispatcher is already running
561 
562 **/
563 EFI_STATUS
MmDispatcher(VOID)564 MmDispatcher (
565   VOID
566   )
567 {
568   EFI_STATUS            Status;
569   LIST_ENTRY            *Link;
570   EFI_MM_DRIVER_ENTRY  *DriverEntry;
571   BOOLEAN               ReadyToRun;
572 
573   DEBUG ((DEBUG_INFO, "MmDispatcher\n"));
574 
575   if (!gRequestDispatch) {
576     DEBUG ((DEBUG_INFO, "  !gRequestDispatch\n"));
577     return EFI_NOT_FOUND;
578   }
579 
580   if (gDispatcherRunning) {
581     DEBUG ((DEBUG_INFO, "  gDispatcherRunning\n"));
582     //
583     // If the dispatcher is running don't let it be restarted.
584     //
585     return EFI_ALREADY_STARTED;
586   }
587 
588   gDispatcherRunning = TRUE;
589 
590   do {
591     //
592     // Drain the Scheduled Queue
593     //
594     DEBUG ((DEBUG_INFO, "  Drain the Scheduled Queue\n"));
595     while (!IsListEmpty (&mScheduledQueue)) {
596       DriverEntry = CR (
597                       mScheduledQueue.ForwardLink,
598                       EFI_MM_DRIVER_ENTRY,
599                       ScheduledLink,
600                       EFI_MM_DRIVER_ENTRY_SIGNATURE
601                       );
602       DEBUG ((DEBUG_INFO, "  DriverEntry (Scheduled) - %g\n", &DriverEntry->FileName));
603 
604       //
605       // Load the MM Driver image into memory. If the Driver was transitioned from
606       // Untrused to Scheduled it would have already been loaded so we may need to
607       // skip the LoadImage
608       //
609       if (DriverEntry->ImageHandle == NULL) {
610         Status = MmLoadImage (DriverEntry);
611 
612         //
613         // Update the driver state to reflect that it's been loaded
614         //
615         if (EFI_ERROR (Status)) {
616           //
617           // The MM Driver could not be loaded, and do not attempt to load or start it again.
618           // Take driver from Scheduled to Initialized.
619           //
620           DriverEntry->Initialized  = TRUE;
621           DriverEntry->Scheduled = FALSE;
622           RemoveEntryList (&DriverEntry->ScheduledLink);
623 
624           //
625           // If it's an error don't try the StartImage
626           //
627           continue;
628         }
629       }
630 
631       DriverEntry->Scheduled    = FALSE;
632       DriverEntry->Initialized  = TRUE;
633       RemoveEntryList (&DriverEntry->ScheduledLink);
634 
635       //
636       // For each MM driver, pass NULL as ImageHandle
637       //
638       if (mEfiSystemTable == NULL) {
639         DEBUG ((DEBUG_INFO, "StartImage - 0x%x (Standalone Mode)\n", DriverEntry->ImageEntryPoint));
640         Status = ((MM_IMAGE_ENTRY_POINT)(UINTN)DriverEntry->ImageEntryPoint) (DriverEntry->ImageHandle, &gMmCoreMmst);
641       } else {
642         DEBUG ((DEBUG_INFO, "StartImage - 0x%x (Tradition Mode)\n", DriverEntry->ImageEntryPoint));
643         Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)DriverEntry->ImageEntryPoint) (
644                                                                DriverEntry->ImageHandle,
645                                                                mEfiSystemTable
646                                                                );
647       }
648       if (EFI_ERROR(Status)) {
649         DEBUG ((DEBUG_INFO, "StartImage Status - %r\n", Status));
650         MmFreePages(DriverEntry->ImageBuffer, DriverEntry->NumberOfPage);
651       }
652     }
653 
654     //
655     // Search DriverList for items to place on Scheduled Queue
656     //
657     DEBUG ((DEBUG_INFO, "  Search DriverList for items to place on Scheduled Queue\n"));
658     ReadyToRun = FALSE;
659     for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {
660       DriverEntry = CR (Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);
661       DEBUG ((DEBUG_INFO, "  DriverEntry (Discovered) - %g\n", &DriverEntry->FileName));
662 
663       if (DriverEntry->DepexProtocolError) {
664         //
665         // If Section Extraction Protocol did not let the Depex be read before retry the read
666         //
667         Status = MmGetDepexSectionAndPreProccess (DriverEntry);
668       }
669 
670       if (DriverEntry->Dependent) {
671         if (MmIsSchedulable (DriverEntry)) {
672           MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);
673           ReadyToRun = TRUE;
674         }
675       }
676     }
677   } while (ReadyToRun);
678 
679   //
680   // If there is no more MM driver to dispatch, stop the dispatch request
681   //
682   DEBUG ((DEBUG_INFO, "  no more MM driver to dispatch, stop the dispatch request\n"));
683   gRequestDispatch = FALSE;
684   for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {
685     DriverEntry = CR (Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);
686     DEBUG ((DEBUG_INFO, "  DriverEntry (Discovered) - %g\n", &DriverEntry->FileName));
687 
688     if (!DriverEntry->Initialized) {
689       //
690       // We have MM driver pending to dispatch
691       //
692       gRequestDispatch = TRUE;
693       break;
694     }
695   }
696 
697   gDispatcherRunning = FALSE;
698 
699   return EFI_SUCCESS;
700 }
701 
702 /**
703   Insert InsertedDriverEntry onto the mScheduledQueue. To do this you
704   must add any driver with a before dependency on InsertedDriverEntry first.
705   You do this by recursively calling this routine. After all the Befores are
706   processed you can add InsertedDriverEntry to the mScheduledQueue.
707   Then you can add any driver with an After dependency on InsertedDriverEntry
708   by recursively calling this routine.
709 
710   @param  InsertedDriverEntry   The driver to insert on the ScheduledLink Queue
711 
712 **/
713 VOID
MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter(IN EFI_MM_DRIVER_ENTRY * InsertedDriverEntry)714 MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (
715   IN  EFI_MM_DRIVER_ENTRY   *InsertedDriverEntry
716   )
717 {
718   LIST_ENTRY            *Link;
719   EFI_MM_DRIVER_ENTRY *DriverEntry;
720 
721   //
722   // Process Before Dependency
723   //
724   for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {
725     DriverEntry = CR(Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);
726     if (DriverEntry->Before && DriverEntry->Dependent && DriverEntry != InsertedDriverEntry) {
727       DEBUG ((DEBUG_DISPATCH, "Evaluate MM DEPEX for FFS(%g)\n", &DriverEntry->FileName));
728       DEBUG ((DEBUG_DISPATCH, "  BEFORE FFS(%g) = ", &DriverEntry->BeforeAfterGuid));
729       if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) {
730         //
731         // Recursively process BEFORE
732         //
733         DEBUG ((DEBUG_DISPATCH, "TRUE\n  END\n  RESULT = TRUE\n"));
734         MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);
735       } else {
736         DEBUG ((DEBUG_DISPATCH, "FALSE\n  END\n  RESULT = FALSE\n"));
737       }
738     }
739   }
740 
741   //
742   // Convert driver from Dependent to Scheduled state
743   //
744 
745   InsertedDriverEntry->Dependent = FALSE;
746   InsertedDriverEntry->Scheduled = TRUE;
747   InsertTailList (&mScheduledQueue, &InsertedDriverEntry->ScheduledLink);
748 
749 
750   //
751   // Process After Dependency
752   //
753   for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {
754     DriverEntry = CR(Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);
755     if (DriverEntry->After && DriverEntry->Dependent && DriverEntry != InsertedDriverEntry) {
756       DEBUG ((DEBUG_DISPATCH, "Evaluate MM DEPEX for FFS(%g)\n", &DriverEntry->FileName));
757       DEBUG ((DEBUG_DISPATCH, "  AFTER FFS(%g) = ", &DriverEntry->BeforeAfterGuid));
758       if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) {
759         //
760         // Recursively process AFTER
761         //
762         DEBUG ((DEBUG_DISPATCH, "TRUE\n  END\n  RESULT = TRUE\n"));
763         MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);
764       } else {
765         DEBUG ((DEBUG_DISPATCH, "FALSE\n  END\n  RESULT = FALSE\n"));
766       }
767     }
768   }
769 }
770 
771 /**
772   Return TRUE if the Fv has been processed, FALSE if not.
773 
774   @param  FvHandle              The handle of a FV that's being tested
775 
776   @retval TRUE                  Fv protocol on FvHandle has been processed
777   @retval FALSE                 Fv protocol on FvHandle has not yet been
778                                 processed
779 
780 **/
781 BOOLEAN
FvHasBeenProcessed(IN EFI_HANDLE FvHandle)782 FvHasBeenProcessed (
783   IN EFI_HANDLE  FvHandle
784   )
785 {
786   LIST_ENTRY    *Link;
787   KNOWN_HANDLE  *KnownHandle;
788 
789   for (Link = mFvHandleList.ForwardLink; Link != &mFvHandleList; Link = Link->ForwardLink) {
790     KnownHandle = CR (Link, KNOWN_HANDLE, Link, KNOWN_HANDLE_SIGNATURE);
791     if (KnownHandle->Handle == FvHandle) {
792       return TRUE;
793     }
794   }
795   return FALSE;
796 }
797 
798 /**
799   Remember that Fv protocol on FvHandle has had it's drivers placed on the
800   mDiscoveredList. This fucntion adds entries on the mFvHandleList. Items are
801   never removed/freed from the mFvHandleList.
802 
803   @param  FvHandle              The handle of a FV that has been processed
804 
805 **/
806 VOID
FvIsBeingProcesssed(IN EFI_HANDLE FvHandle)807 FvIsBeingProcesssed (
808   IN EFI_HANDLE  FvHandle
809   )
810 {
811   KNOWN_HANDLE  *KnownHandle;
812 
813   DEBUG ((DEBUG_INFO, "FvIsBeingProcesssed - 0x%08x\n", FvHandle));
814 
815   KnownHandle = AllocatePool (sizeof (KNOWN_HANDLE));
816   ASSERT (KnownHandle != NULL);
817 
818   KnownHandle->Signature = KNOWN_HANDLE_SIGNATURE;
819   KnownHandle->Handle = FvHandle;
820   InsertTailList (&mFvHandleList, &KnownHandle->Link);
821 }
822 
823 /**
824   Add an entry to the mDiscoveredList. Allocate memory to store the DriverEntry,
825   and initilize any state variables. Read the Depex from the FV and store it
826   in DriverEntry. Pre-process the Depex to set the Before and After state.
827   The Discovered list is never free'ed and contains booleans that represent the
828   other possible MM driver states.
829 
830   @param  Fv                    Fv protocol, needed to read Depex info out of
831                                 FLASH.
832   @param  FvHandle              Handle for Fv, needed in the
833                                 EFI_MM_DRIVER_ENTRY so that the PE image can be
834                                 read out of the FV at a later time.
835   @param  DriverName            Name of driver to add to mDiscoveredList.
836 
837   @retval EFI_SUCCESS           If driver was added to the mDiscoveredList.
838   @retval EFI_ALREADY_STARTED   The driver has already been started. Only one
839                                 DriverName may be active in the system at any one
840                                 time.
841 
842 **/
843 EFI_STATUS
MmAddToDriverList(IN EFI_HANDLE FvHandle,IN VOID * Pe32Data,IN UINTN Pe32DataSize,IN VOID * Depex,IN UINTN DepexSize,IN EFI_GUID * DriverName)844 MmAddToDriverList (
845   IN EFI_HANDLE   FvHandle,
846   IN VOID         *Pe32Data,
847   IN UINTN        Pe32DataSize,
848   IN VOID         *Depex,
849   IN UINTN        DepexSize,
850   IN EFI_GUID     *DriverName
851   )
852 {
853   EFI_MM_DRIVER_ENTRY  *DriverEntry;
854 
855   DEBUG ((DEBUG_INFO, "MmAddToDriverList - %g (0x%08x)\n", DriverName, Pe32Data));
856 
857   //
858   // Create the Driver Entry for the list. ZeroPool initializes lots of variables to
859   // NULL or FALSE.
860   //
861   DriverEntry = AllocateZeroPool (sizeof (EFI_MM_DRIVER_ENTRY));
862   ASSERT (DriverEntry != NULL);
863 
864   DriverEntry->Signature        = EFI_MM_DRIVER_ENTRY_SIGNATURE;
865   CopyGuid (&DriverEntry->FileName, DriverName);
866   DriverEntry->FvHandle         = FvHandle;
867   DriverEntry->Pe32Data         = Pe32Data;
868   DriverEntry->Pe32DataSize     = Pe32DataSize;
869   DriverEntry->Depex            = Depex;
870   DriverEntry->DepexSize        = DepexSize;
871 
872   MmGetDepexSectionAndPreProccess (DriverEntry);
873 
874   InsertTailList (&mDiscoveredList, &DriverEntry->Link);
875   gRequestDispatch = TRUE;
876 
877   return EFI_SUCCESS;
878 }
879 
880 /**
881   Traverse the discovered list for any drivers that were discovered but not loaded
882   because the dependency experessions evaluated to false.
883 
884 **/
885 VOID
MmDisplayDiscoveredNotDispatched(VOID)886 MmDisplayDiscoveredNotDispatched (
887   VOID
888   )
889 {
890   LIST_ENTRY                   *Link;
891   EFI_MM_DRIVER_ENTRY         *DriverEntry;
892 
893   for (Link = mDiscoveredList.ForwardLink;Link !=&mDiscoveredList; Link = Link->ForwardLink) {
894     DriverEntry = CR (Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);
895     if (DriverEntry->Dependent) {
896       DEBUG ((DEBUG_LOAD, "MM Driver %g was discovered but not loaded!!\n", &DriverEntry->FileName));
897     }
898   }
899 }
900