StandaloneMmPkg/Core/Dispatcher: don't copy dispatched image twice
[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   UINTN                          PageCount;\r
298   EFI_STATUS                     Status;\r
299   EFI_PHYSICAL_ADDRESS           DstBuffer;\r
300   PE_COFF_LOADER_IMAGE_CONTEXT   ImageContext;\r
301 \r
302   DEBUG ((DEBUG_INFO, "MmLoadImage - %g\n", &DriverEntry->FileName));\r
303 \r
304   Status               = EFI_SUCCESS;\r
305 \r
306   //\r
307   // Initialize ImageContext\r
308   //\r
309   ImageContext.Handle = DriverEntry->Pe32Data;\r
310   ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;\r
311 \r
312   //\r
313   // Get information about the image being loaded\r
314   //\r
315   Status = PeCoffLoaderGetImageInfo (&ImageContext);\r
316   if (EFI_ERROR (Status)) {\r
317     return Status;\r
318   }\r
319 \r
320   PageCount = (UINTN)EFI_SIZE_TO_PAGES ((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment);\r
321   DstBuffer = (UINTN)(-1);\r
322 \r
323   Status = MmAllocatePages (\r
324              AllocateMaxAddress,\r
325              EfiRuntimeServicesCode,\r
326              PageCount,\r
327              &DstBuffer\r
328              );\r
329   if (EFI_ERROR (Status)) {\r
330     return Status;\r
331   }\r
332 \r
333   ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer;\r
334 \r
335   //\r
336   // Align buffer on section boundry\r
337   //\r
338   ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;\r
339   ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext.SectionAlignment - 1));\r
340 \r
341   //\r
342   // Load the image to our new buffer\r
343   //\r
344   Status = PeCoffLoaderLoadImage (&ImageContext);\r
345   if (EFI_ERROR (Status)) {\r
346     MmFreePages (DstBuffer, PageCount);\r
347     return Status;\r
348   }\r
349 \r
350   //\r
351   // Relocate the image in our new buffer\r
352   //\r
353   Status = PeCoffLoaderRelocateImage (&ImageContext);\r
354   if (EFI_ERROR (Status)) {\r
355     MmFreePages (DstBuffer, PageCount);\r
356     return Status;\r
357   }\r
358 \r
359   //\r
360   // Flush the instruction cache so the image data are written before we execute it\r
361   //\r
362   InvalidateInstructionCacheRange ((VOID *)(UINTN) ImageContext.ImageAddress, (UINTN) ImageContext.ImageSize);\r
363 \r
364   //\r
365   // Save Image EntryPoint in DriverEntry\r
366   //\r
367   DriverEntry->ImageEntryPoint  = ImageContext.EntryPoint;\r
368   DriverEntry->ImageBuffer      = DstBuffer;\r
369   DriverEntry->NumberOfPage     = PageCount;\r
370 \r
371   if (mEfiSystemTable != NULL) {\r
372     Status = mEfiSystemTable->BootServices->AllocatePool (\r
373                                               EfiBootServicesData,\r
374                                               sizeof (EFI_LOADED_IMAGE_PROTOCOL),\r
375                                               (VOID **)&DriverEntry->LoadedImage\r
376                                               );\r
377     if (EFI_ERROR (Status)) {\r
378       MmFreePages (DstBuffer, PageCount);\r
379       return Status;\r
380     }\r
381 \r
382     ZeroMem (DriverEntry->LoadedImage, sizeof (EFI_LOADED_IMAGE_PROTOCOL));\r
383     //\r
384     // Fill in the remaining fields of the Loaded Image Protocol instance.\r
385     // Note: ImageBase is an SMRAM address that can not be accessed outside of SMRAM if SMRAM window is closed.\r
386     //\r
387     DriverEntry->LoadedImage->Revision      = EFI_LOADED_IMAGE_PROTOCOL_REVISION;\r
388     DriverEntry->LoadedImage->ParentHandle  = NULL;\r
389     DriverEntry->LoadedImage->SystemTable   = mEfiSystemTable;\r
390     DriverEntry->LoadedImage->DeviceHandle  = NULL;\r
391     DriverEntry->LoadedImage->FilePath      = NULL;\r
392 \r
393     DriverEntry->LoadedImage->ImageBase     = (VOID *)(UINTN)DriverEntry->ImageBuffer;\r
394     DriverEntry->LoadedImage->ImageSize     = ImageContext.ImageSize;\r
395     DriverEntry->LoadedImage->ImageCodeType = EfiRuntimeServicesCode;\r
396     DriverEntry->LoadedImage->ImageDataType = EfiRuntimeServicesData;\r
397 \r
398     //\r
399     // Create a new image handle in the UEFI handle database for the MM Driver\r
400     //\r
401     DriverEntry->ImageHandle = NULL;\r
402     Status = mEfiSystemTable->BootServices->InstallMultipleProtocolInterfaces (\r
403                                               &DriverEntry->ImageHandle,\r
404                                               &gEfiLoadedImageProtocolGuid,\r
405                                               DriverEntry->LoadedImage,\r
406                                               NULL\r
407                                               );\r
408   }\r
409 \r
410   //\r
411   // Print the load address and the PDB file name if it is available\r
412   //\r
413   DEBUG_CODE_BEGIN ();\r
414 \r
415   UINTN Index;\r
416   UINTN StartIndex;\r
417   CHAR8 EfiFileName[256];\r
418 \r
419   DEBUG ((DEBUG_INFO | DEBUG_LOAD,\r
420           "Loading MM driver at 0x%11p EntryPoint=0x%11p ",\r
421           (VOID *)(UINTN) ImageContext.ImageAddress,\r
422           FUNCTION_ENTRY_POINT (ImageContext.EntryPoint)));\r
423 \r
424   //\r
425   // Print Module Name by Pdb file path.\r
426   // Windows and Unix style file path are all trimmed correctly.\r
427   //\r
428   if (ImageContext.PdbPointer != NULL) {\r
429     StartIndex = 0;\r
430     for (Index = 0; ImageContext.PdbPointer[Index] != 0; Index++) {\r
431       if ((ImageContext.PdbPointer[Index] == '\\') || (ImageContext.PdbPointer[Index] == '/')) {\r
432         StartIndex = Index + 1;\r
433       }\r
434     }\r
435 \r
436     //\r
437     // Copy the PDB file name to our temporary string, and replace .pdb with .efi\r
438     // The PDB file name is limited in the range of 0~255.\r
439     // If the length is bigger than 255, trim the redudant characters to avoid overflow in array boundary.\r
440     //\r
441     for (Index = 0; Index < sizeof (EfiFileName) - 4; Index++) {\r
442       EfiFileName[Index] = ImageContext.PdbPointer[Index + StartIndex];\r
443       if (EfiFileName[Index] == 0) {\r
444         EfiFileName[Index] = '.';\r
445       }\r
446       if (EfiFileName[Index] == '.') {\r
447         EfiFileName[Index + 1] = 'e';\r
448         EfiFileName[Index + 2] = 'f';\r
449         EfiFileName[Index + 3] = 'i';\r
450         EfiFileName[Index + 4] = 0;\r
451         break;\r
452       }\r
453     }\r
454 \r
455     if (Index == sizeof (EfiFileName) - 4) {\r
456       EfiFileName[Index] = 0;\r
457     }\r
458     DEBUG ((DEBUG_INFO | DEBUG_LOAD, "%a", EfiFileName));\r
459   }\r
460   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "\n"));\r
461 \r
462   DEBUG_CODE_END ();\r
463 \r
464   return Status;\r
465 }\r
466 \r
467 /**\r
468   Preprocess dependency expression and update DriverEntry to reflect the\r
469   state of  Before and After dependencies. If DriverEntry->Before\r
470   or DriverEntry->After is set it will never be cleared.\r
471 \r
472   @param  DriverEntry           DriverEntry element to update .\r
473 \r
474   @retval EFI_SUCCESS           It always works.\r
475 \r
476 **/\r
477 EFI_STATUS\r
478 MmPreProcessDepex (\r
479   IN EFI_MM_DRIVER_ENTRY  *DriverEntry\r
480   )\r
481 {\r
482   UINT8  *Iterator;\r
483 \r
484   Iterator = DriverEntry->Depex;\r
485   DriverEntry->Dependent = TRUE;\r
486 \r
487   if (*Iterator == EFI_DEP_BEFORE) {\r
488     DriverEntry->Before = TRUE;\r
489   } else if (*Iterator == EFI_DEP_AFTER) {\r
490     DriverEntry->After = TRUE;\r
491   }\r
492 \r
493   if (DriverEntry->Before || DriverEntry->After) {\r
494     CopyMem (&DriverEntry->BeforeAfterGuid, Iterator + 1, sizeof (EFI_GUID));\r
495   }\r
496 \r
497   return EFI_SUCCESS;\r
498 }\r
499 \r
500 /**\r
501   Read Depex and pre-process the Depex for Before and After. If Section Extraction\r
502   protocol returns an error via ReadSection defer the reading of the Depex.\r
503 \r
504   @param  DriverEntry           Driver to work on.\r
505 \r
506   @retval EFI_SUCCESS           Depex read and preprossesed\r
507   @retval EFI_PROTOCOL_ERROR    The section extraction protocol returned an error\r
508                                 and  Depex reading needs to be retried.\r
509   @retval Error                 DEPEX not found.\r
510 \r
511 **/\r
512 EFI_STATUS\r
513 MmGetDepexSectionAndPreProccess (\r
514   IN EFI_MM_DRIVER_ENTRY  *DriverEntry\r
515   )\r
516 {\r
517   EFI_STATUS                     Status;\r
518 \r
519   //\r
520   // Data already read\r
521   //\r
522   if (DriverEntry->Depex == NULL) {\r
523     Status = EFI_NOT_FOUND;\r
524   } else {\r
525     Status = EFI_SUCCESS;\r
526   }\r
527   if (EFI_ERROR (Status)) {\r
528     if (Status == EFI_PROTOCOL_ERROR) {\r
529       //\r
530       // The section extraction protocol failed so set protocol error flag\r
531       //\r
532       DriverEntry->DepexProtocolError = TRUE;\r
533     } else {\r
534       //\r
535       // If no Depex assume depend on all architectural protocols\r
536       //\r
537       DriverEntry->Depex = NULL;\r
538       DriverEntry->Dependent = TRUE;\r
539       DriverEntry->DepexProtocolError = FALSE;\r
540     }\r
541   } else {\r
542     //\r
543     // Set Before and After state information based on Depex\r
544     // Driver will be put in Dependent state\r
545     //\r
546     MmPreProcessDepex (DriverEntry);\r
547     DriverEntry->DepexProtocolError = FALSE;\r
548   }\r
549 \r
550   return Status;\r
551 }\r
552 \r
553 /**\r
554   This is the main Dispatcher for MM and it exits when there are no more\r
555   drivers to run. Drain the mScheduledQueue and load and start a PE\r
556   image for each driver. Search the mDiscoveredList to see if any driver can\r
557   be placed on the mScheduledQueue. If no drivers are placed on the\r
558   mScheduledQueue exit the function.\r
559 \r
560   @retval EFI_SUCCESS           All of the MM Drivers that could be dispatched\r
561                                 have been run and the MM Entry Point has been\r
562                                 registered.\r
563   @retval EFI_NOT_READY         The MM Driver that registered the MM Entry Point\r
564                                 was just dispatched.\r
565   @retval EFI_NOT_FOUND         There are no MM Drivers available to be dispatched.\r
566   @retval EFI_ALREADY_STARTED   The MM Dispatcher is already running\r
567 \r
568 **/\r
569 EFI_STATUS\r
570 MmDispatcher (\r
571   VOID\r
572   )\r
573 {\r
574   EFI_STATUS            Status;\r
575   LIST_ENTRY            *Link;\r
576   EFI_MM_DRIVER_ENTRY  *DriverEntry;\r
577   BOOLEAN               ReadyToRun;\r
578   BOOLEAN               PreviousMmEntryPointRegistered;\r
579 \r
580   DEBUG ((DEBUG_INFO, "MmDispatcher\n"));\r
581 \r
582   if (!gRequestDispatch) {\r
583     DEBUG ((DEBUG_INFO, "  !gRequestDispatch\n"));\r
584     return EFI_NOT_FOUND;\r
585   }\r
586 \r
587   if (gDispatcherRunning) {\r
588     DEBUG ((DEBUG_INFO, "  gDispatcherRunning\n"));\r
589     //\r
590     // If the dispatcher is running don't let it be restarted.\r
591     //\r
592     return EFI_ALREADY_STARTED;\r
593   }\r
594 \r
595   gDispatcherRunning = TRUE;\r
596 \r
597   do {\r
598     //\r
599     // Drain the Scheduled Queue\r
600     //\r
601     DEBUG ((DEBUG_INFO, "  Drain the Scheduled Queue\n"));\r
602     while (!IsListEmpty (&mScheduledQueue)) {\r
603       DriverEntry = CR (\r
604                       mScheduledQueue.ForwardLink,\r
605                       EFI_MM_DRIVER_ENTRY,\r
606                       ScheduledLink,\r
607                       EFI_MM_DRIVER_ENTRY_SIGNATURE\r
608                       );\r
609       DEBUG ((DEBUG_INFO, "  DriverEntry (Scheduled) - %g\n", &DriverEntry->FileName));\r
610 \r
611       //\r
612       // Load the MM Driver image into memory. If the Driver was transitioned from\r
613       // Untrused to Scheduled it would have already been loaded so we may need to\r
614       // skip the LoadImage\r
615       //\r
616       if (DriverEntry->ImageHandle == NULL) {\r
617         Status = MmLoadImage (DriverEntry);\r
618 \r
619         //\r
620         // Update the driver state to reflect that it's been loaded\r
621         //\r
622         if (EFI_ERROR (Status)) {\r
623           //\r
624           // The MM Driver could not be loaded, and do not attempt to load or start it again.\r
625           // Take driver from Scheduled to Initialized.\r
626           //\r
627           DriverEntry->Initialized  = TRUE;\r
628           DriverEntry->Scheduled = FALSE;\r
629           RemoveEntryList (&DriverEntry->ScheduledLink);\r
630 \r
631           //\r
632           // If it's an error don't try the StartImage\r
633           //\r
634           continue;\r
635         }\r
636       }\r
637 \r
638       DriverEntry->Scheduled    = FALSE;\r
639       DriverEntry->Initialized  = TRUE;\r
640       RemoveEntryList (&DriverEntry->ScheduledLink);\r
641 \r
642       //\r
643       // Cache state of MmEntryPointRegistered before calling entry point\r
644       //\r
645       PreviousMmEntryPointRegistered = gMmCorePrivate->MmEntryPointRegistered;\r
646 \r
647       //\r
648       // For each MM driver, pass NULL as ImageHandle\r
649       //\r
650       if (mEfiSystemTable == NULL) {\r
651         DEBUG ((DEBUG_INFO, "StartImage - 0x%x (Standalone Mode)\n", DriverEntry->ImageEntryPoint));\r
652         Status = ((MM_IMAGE_ENTRY_POINT)(UINTN)DriverEntry->ImageEntryPoint) (DriverEntry->ImageHandle, &gMmCoreMmst);\r
653       } else {\r
654         DEBUG ((DEBUG_INFO, "StartImage - 0x%x (Tradition Mode)\n", DriverEntry->ImageEntryPoint));\r
655         Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)DriverEntry->ImageEntryPoint) (\r
656                                                                DriverEntry->ImageHandle,\r
657                                                                mEfiSystemTable\r
658                                                                );\r
659       }\r
660       if (EFI_ERROR(Status)) {\r
661         DEBUG ((DEBUG_INFO, "StartImage Status - %r\n", Status));\r
662         MmFreePages(DriverEntry->ImageBuffer, DriverEntry->NumberOfPage);\r
663       }\r
664 \r
665       if (!PreviousMmEntryPointRegistered && gMmCorePrivate->MmEntryPointRegistered) {\r
666         //\r
667         // Return immediately if the MM Entry Point was registered by the MM\r
668         // Driver that was just dispatched.  The MM IPL will reinvoke the MM\r
669         // Core Dispatcher.  This is required so MM Mode may be enabled as soon\r
670         // as all the dependent MM Drivers for MM Mode have been dispatched.\r
671         // Once the MM Entry Point has been registered, then MM Mode will be\r
672         // used.\r
673         //\r
674         gRequestDispatch = TRUE;\r
675         gDispatcherRunning = FALSE;\r
676         return EFI_NOT_READY;\r
677       }\r
678     }\r
679 \r
680     //\r
681     // Search DriverList for items to place on Scheduled Queue\r
682     //\r
683     DEBUG ((DEBUG_INFO, "  Search DriverList for items to place on Scheduled Queue\n"));\r
684     ReadyToRun = FALSE;\r
685     for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {\r
686       DriverEntry = CR (Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);\r
687       DEBUG ((DEBUG_INFO, "  DriverEntry (Discovered) - %g\n", &DriverEntry->FileName));\r
688 \r
689       if (DriverEntry->DepexProtocolError) {\r
690         //\r
691         // If Section Extraction Protocol did not let the Depex be read before retry the read\r
692         //\r
693         Status = MmGetDepexSectionAndPreProccess (DriverEntry);\r
694       }\r
695 \r
696       if (DriverEntry->Dependent) {\r
697         if (MmIsSchedulable (DriverEntry)) {\r
698           MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);\r
699           ReadyToRun = TRUE;\r
700         }\r
701       }\r
702     }\r
703   } while (ReadyToRun);\r
704 \r
705   //\r
706   // If there is no more MM driver to dispatch, stop the dispatch request\r
707   //\r
708   DEBUG ((DEBUG_INFO, "  no more MM driver to dispatch, stop the dispatch request\n"));\r
709   gRequestDispatch = FALSE;\r
710   for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {\r
711     DriverEntry = CR (Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);\r
712     DEBUG ((DEBUG_INFO, "  DriverEntry (Discovered) - %g\n", &DriverEntry->FileName));\r
713 \r
714     if (!DriverEntry->Initialized) {\r
715       //\r
716       // We have MM driver pending to dispatch\r
717       //\r
718       gRequestDispatch = TRUE;\r
719       break;\r
720     }\r
721   }\r
722 \r
723   gDispatcherRunning = FALSE;\r
724 \r
725   return EFI_SUCCESS;\r
726 }\r
727 \r
728 /**\r
729   Insert InsertedDriverEntry onto the mScheduledQueue. To do this you\r
730   must add any driver with a before dependency on InsertedDriverEntry first.\r
731   You do this by recursively calling this routine. After all the Befores are\r
732   processed you can add InsertedDriverEntry to the mScheduledQueue.\r
733   Then you can add any driver with an After dependency on InsertedDriverEntry\r
734   by recursively calling this routine.\r
735 \r
736   @param  InsertedDriverEntry   The driver to insert on the ScheduledLink Queue\r
737 \r
738 **/\r
739 VOID\r
740 MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (\r
741   IN  EFI_MM_DRIVER_ENTRY   *InsertedDriverEntry\r
742   )\r
743 {\r
744   LIST_ENTRY            *Link;\r
745   EFI_MM_DRIVER_ENTRY *DriverEntry;\r
746 \r
747   //\r
748   // Process Before Dependency\r
749   //\r
750   for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {\r
751     DriverEntry = CR(Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);\r
752     if (DriverEntry->Before && DriverEntry->Dependent && DriverEntry != InsertedDriverEntry) {\r
753       DEBUG ((DEBUG_DISPATCH, "Evaluate MM DEPEX for FFS(%g)\n", &DriverEntry->FileName));\r
754       DEBUG ((DEBUG_DISPATCH, "  BEFORE FFS(%g) = ", &DriverEntry->BeforeAfterGuid));\r
755       if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) {\r
756         //\r
757         // Recursively process BEFORE\r
758         //\r
759         DEBUG ((DEBUG_DISPATCH, "TRUE\n  END\n  RESULT = TRUE\n"));\r
760         MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);\r
761       } else {\r
762         DEBUG ((DEBUG_DISPATCH, "FALSE\n  END\n  RESULT = FALSE\n"));\r
763       }\r
764     }\r
765   }\r
766 \r
767   //\r
768   // Convert driver from Dependent to Scheduled state\r
769   //\r
770 \r
771   InsertedDriverEntry->Dependent = FALSE;\r
772   InsertedDriverEntry->Scheduled = TRUE;\r
773   InsertTailList (&mScheduledQueue, &InsertedDriverEntry->ScheduledLink);\r
774 \r
775 \r
776   //\r
777   // Process After Dependency\r
778   //\r
779   for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {\r
780     DriverEntry = CR(Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);\r
781     if (DriverEntry->After && DriverEntry->Dependent && DriverEntry != InsertedDriverEntry) {\r
782       DEBUG ((DEBUG_DISPATCH, "Evaluate MM DEPEX for FFS(%g)\n", &DriverEntry->FileName));\r
783       DEBUG ((DEBUG_DISPATCH, "  AFTER FFS(%g) = ", &DriverEntry->BeforeAfterGuid));\r
784       if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) {\r
785         //\r
786         // Recursively process AFTER\r
787         //\r
788         DEBUG ((DEBUG_DISPATCH, "TRUE\n  END\n  RESULT = TRUE\n"));\r
789         MmInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);\r
790       } else {\r
791         DEBUG ((DEBUG_DISPATCH, "FALSE\n  END\n  RESULT = FALSE\n"));\r
792       }\r
793     }\r
794   }\r
795 }\r
796 \r
797 /**\r
798   Return TRUE if the Fv has been processed, FALSE if not.\r
799 \r
800   @param  FvHandle              The handle of a FV that's being tested\r
801 \r
802   @retval TRUE                  Fv protocol on FvHandle has been processed\r
803   @retval FALSE                 Fv protocol on FvHandle has not yet been\r
804                                 processed\r
805 \r
806 **/\r
807 BOOLEAN\r
808 FvHasBeenProcessed (\r
809   IN EFI_HANDLE  FvHandle\r
810   )\r
811 {\r
812   LIST_ENTRY    *Link;\r
813   KNOWN_HANDLE  *KnownHandle;\r
814 \r
815   for (Link = mFvHandleList.ForwardLink; Link != &mFvHandleList; Link = Link->ForwardLink) {\r
816     KnownHandle = CR (Link, KNOWN_HANDLE, Link, KNOWN_HANDLE_SIGNATURE);\r
817     if (KnownHandle->Handle == FvHandle) {\r
818       return TRUE;\r
819     }\r
820   }\r
821   return FALSE;\r
822 }\r
823 \r
824 /**\r
825   Remember that Fv protocol on FvHandle has had it's drivers placed on the\r
826   mDiscoveredList. This fucntion adds entries on the mFvHandleList. Items are\r
827   never removed/freed from the mFvHandleList.\r
828 \r
829   @param  FvHandle              The handle of a FV that has been processed\r
830 \r
831 **/\r
832 VOID\r
833 FvIsBeingProcesssed (\r
834   IN EFI_HANDLE  FvHandle\r
835   )\r
836 {\r
837   KNOWN_HANDLE  *KnownHandle;\r
838 \r
839   DEBUG ((DEBUG_INFO, "FvIsBeingProcesssed - 0x%08x\n", FvHandle));\r
840 \r
841   KnownHandle = AllocatePool (sizeof (KNOWN_HANDLE));\r
842   ASSERT (KnownHandle != NULL);\r
843 \r
844   KnownHandle->Signature = KNOWN_HANDLE_SIGNATURE;\r
845   KnownHandle->Handle = FvHandle;\r
846   InsertTailList (&mFvHandleList, &KnownHandle->Link);\r
847 }\r
848 \r
849 /**\r
850   Add an entry to the mDiscoveredList. Allocate memory to store the DriverEntry,\r
851   and initilize any state variables. Read the Depex from the FV and store it\r
852   in DriverEntry. Pre-process the Depex to set the Before and After state.\r
853   The Discovered list is never free'ed and contains booleans that represent the\r
854   other possible MM driver states.\r
855 \r
856   @param  Fv                    Fv protocol, needed to read Depex info out of\r
857                                 FLASH.\r
858   @param  FvHandle              Handle for Fv, needed in the\r
859                                 EFI_MM_DRIVER_ENTRY so that the PE image can be\r
860                                 read out of the FV at a later time.\r
861   @param  DriverName            Name of driver to add to mDiscoveredList.\r
862 \r
863   @retval EFI_SUCCESS           If driver was added to the mDiscoveredList.\r
864   @retval EFI_ALREADY_STARTED   The driver has already been started. Only one\r
865                                 DriverName may be active in the system at any one\r
866                                 time.\r
867 \r
868 **/\r
869 EFI_STATUS\r
870 MmAddToDriverList (\r
871   IN EFI_HANDLE   FvHandle,\r
872   IN VOID         *Pe32Data,\r
873   IN UINTN        Pe32DataSize,\r
874   IN VOID         *Depex,\r
875   IN UINTN        DepexSize,\r
876   IN EFI_GUID     *DriverName\r
877   )\r
878 {\r
879   EFI_MM_DRIVER_ENTRY  *DriverEntry;\r
880 \r
881   DEBUG ((DEBUG_INFO, "MmAddToDriverList - %g (0x%08x)\n", DriverName, Pe32Data));\r
882 \r
883   //\r
884   // Create the Driver Entry for the list. ZeroPool initializes lots of variables to\r
885   // NULL or FALSE.\r
886   //\r
887   DriverEntry = AllocateZeroPool (sizeof (EFI_MM_DRIVER_ENTRY));\r
888   ASSERT (DriverEntry != NULL);\r
889 \r
890   DriverEntry->Signature        = EFI_MM_DRIVER_ENTRY_SIGNATURE;\r
891   CopyGuid (&DriverEntry->FileName, DriverName);\r
892   DriverEntry->FvHandle         = FvHandle;\r
893   DriverEntry->Pe32Data         = Pe32Data;\r
894   DriverEntry->Pe32DataSize     = Pe32DataSize;\r
895   DriverEntry->Depex            = Depex;\r
896   DriverEntry->DepexSize        = DepexSize;\r
897 \r
898   MmGetDepexSectionAndPreProccess (DriverEntry);\r
899 \r
900   InsertTailList (&mDiscoveredList, &DriverEntry->Link);\r
901   gRequestDispatch = TRUE;\r
902 \r
903   return EFI_SUCCESS;\r
904 }\r
905 \r
906 /**\r
907   This function is the main entry point for an MM handler dispatch\r
908   or communicate-based callback.\r
909 \r
910   Event notification that is fired every time a FV dispatch protocol is added.\r
911   More than one protocol may have been added when this event is fired, so you\r
912   must loop on MmLocateHandle () to see how many protocols were added and\r
913   do the following to each FV:\r
914   If the Fv has already been processed, skip it. If the Fv has not been\r
915   processed then mark it as being processed, as we are about to process it.\r
916   Read the Fv and add any driver in the Fv to the mDiscoveredList.The\r
917   mDiscoveredList is never free'ed and contains variables that define\r
918   the other states the MM driver transitions to..\r
919   While you are at it read the A Priori file into memory.\r
920   Place drivers in the A Priori list onto the mScheduledQueue.\r
921 \r
922   @param  DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().\r
923   @param  Context         Points to an optional handler context which was specified when the handler was registered.\r
924   @param  CommBuffer      A pointer to a collection of data in memory that will\r
925                           be conveyed from a non-MM environment into an MM environment.\r
926   @param  CommBufferSize  The size of the CommBuffer.\r
927 \r
928   @return Status Code\r
929 \r
930 **/\r
931 EFI_STATUS\r
932 EFIAPI\r
933 MmDriverDispatchHandler (\r
934   IN     EFI_HANDLE  DispatchHandle,\r
935   IN     CONST VOID  *Context,        OPTIONAL\r
936   IN OUT VOID        *CommBuffer,     OPTIONAL\r
937   IN OUT UINTN       *CommBufferSize  OPTIONAL\r
938   )\r
939 {\r
940   EFI_STATUS                            Status;\r
941 \r
942   DEBUG ((DEBUG_INFO, "MmDriverDispatchHandler\n"));\r
943 \r
944   //\r
945   // Execute the MM Dispatcher on any newly discovered FVs and previously\r
946   // discovered MM drivers that have been discovered but not dispatched.\r
947   //\r
948   Status = MmDispatcher ();\r
949 \r
950   //\r
951   // Check to see if CommBuffer and CommBufferSize are valid\r
952   //\r
953   if (CommBuffer != NULL && CommBufferSize != NULL) {\r
954     if (*CommBufferSize > 0) {\r
955       if (Status == EFI_NOT_READY) {\r
956         //\r
957         // If a the MM Core Entry Point was just registered, then set flag to\r
958         // request the MM Dispatcher to be restarted.\r
959         //\r
960         *(UINT8 *)CommBuffer = COMM_BUFFER_MM_DISPATCH_RESTART;\r
961       } else if (!EFI_ERROR (Status)) {\r
962         //\r
963         // Set the flag to show that the MM Dispatcher executed without errors\r
964         //\r
965         *(UINT8 *)CommBuffer = COMM_BUFFER_MM_DISPATCH_SUCCESS;\r
966       } else {\r
967         //\r
968         // Set the flag to show that the MM Dispatcher encountered an error\r
969         //\r
970         *(UINT8 *)CommBuffer = COMM_BUFFER_MM_DISPATCH_ERROR;\r
971       }\r
972     }\r
973   }\r
974 \r
975   return EFI_SUCCESS;\r
976 }\r
977 \r
978 /**\r
979   This function is the main entry point for an MM handler dispatch\r
980   or communicate-based callback.\r
981 \r
982   @param  DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().\r
983   @param  Context         Points to an optional handler context which was specified when the handler was registered.\r
984   @param  CommBuffer      A pointer to a collection of data in memory that will\r
985                           be conveyed from a non-MM environment into an MM environment.\r
986   @param  CommBufferSize  The size of the CommBuffer.\r
987 \r
988   @return Status Code\r
989 \r
990 **/\r
991 EFI_STATUS\r
992 EFIAPI\r
993 MmFvDispatchHandler (\r
994   IN     EFI_HANDLE               DispatchHandle,\r
995   IN     CONST VOID               *Context,        OPTIONAL\r
996   IN OUT VOID                     *CommBuffer,     OPTIONAL\r
997   IN OUT UINTN                    *CommBufferSize  OPTIONAL\r
998   )\r
999 {\r
1000   EFI_STATUS                            Status;\r
1001   EFI_MM_COMMUNICATE_FV_DISPATCH_DATA  *CommunicationFvDispatchData;\r
1002   EFI_FIRMWARE_VOLUME_HEADER            *FwVolHeader;\r
1003 \r
1004   DEBUG ((DEBUG_INFO, "MmFvDispatchHandler\n"));\r
1005 \r
1006   CommunicationFvDispatchData = CommBuffer;\r
1007 \r
1008   DEBUG ((DEBUG_INFO, "  Dispatch - 0x%016lx - 0x%016lx\n", CommunicationFvDispatchData->Address,\r
1009           CommunicationFvDispatchData->Size));\r
1010 \r
1011   FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)CommunicationFvDispatchData->Address;\r
1012 \r
1013   MmCoreFfsFindMmDriver (FwVolHeader);\r
1014 \r
1015   //\r
1016   // Execute the MM Dispatcher on any newly discovered FVs and previously\r
1017   // discovered MM drivers that have been discovered but not dispatched.\r
1018   //\r
1019   Status = MmDispatcher ();\r
1020 \r
1021   return Status;\r
1022 }\r
1023 \r
1024 /**\r
1025   Traverse the discovered list for any drivers that were discovered but not loaded\r
1026   because the dependency experessions evaluated to false.\r
1027 \r
1028 **/\r
1029 VOID\r
1030 MmDisplayDiscoveredNotDispatched (\r
1031   VOID\r
1032   )\r
1033 {\r
1034   LIST_ENTRY                   *Link;\r
1035   EFI_MM_DRIVER_ENTRY         *DriverEntry;\r
1036 \r
1037   for (Link = mDiscoveredList.ForwardLink;Link !=&mDiscoveredList; Link = Link->ForwardLink) {\r
1038     DriverEntry = CR (Link, EFI_MM_DRIVER_ENTRY, Link, EFI_MM_DRIVER_ENTRY_SIGNATURE);\r
1039     if (DriverEntry->Dependent) {\r
1040       DEBUG ((DEBUG_LOAD, "MM Driver %g was discovered but not loaded!!\n", &DriverEntry->FileName));\r
1041     }\r
1042   }\r
1043 }\r