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