| /** @file | |
| DXE Dispatcher. | |
| Step #1 - When a FV protocol is added to the system every driver in the FV | |
| is added to the mDiscoveredList. The SOR, Before, and After Depex are | |
| pre-processed as drivers are added to the mDiscoveredList. If an Apriori | |
| file exists in the FV those drivers are addeded to the | |
| mScheduledQueue. The mFvHandleList is used to make sure a | |
| FV is only processed once. | |
| Step #2 - Dispatch. Remove driver from the mScheduledQueue and load and | |
| start it. After mScheduledQueue is drained check the | |
| mDiscoveredList to see if any item has a Depex that is ready to | |
| be placed on the mScheduledQueue. | |
| Step #3 - Adding to the mScheduledQueue requires that you process Before | |
| and After dependencies. This is done recursively as the call to add | |
| to the mScheduledQueue checks for Before and recursively adds | |
| all Befores. It then addes the item that was passed in and then | |
| processess the After dependecies by recursively calling the routine. | |
| Dispatcher Rules: | |
| The rules for the dispatcher are in chapter 10 of the DXE CIS. Figure 10-3 | |
| is the state diagram for the DXE dispatcher | |
| Depex - Dependency Expresion. | |
| SOR - Schedule On Request - Don't schedule if this bit is set. | |
| Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "DxeMain.h" | |
| // | |
| // The Driver List contains one copy of every driver that has been discovered. | |
| // Items are never removed from the driver list. List of EFI_CORE_DRIVER_ENTRY | |
| // | |
| LIST_ENTRY mDiscoveredList = INITIALIZE_LIST_HEAD_VARIABLE (mDiscoveredList); | |
| // | |
| // Queue of drivers that are ready to dispatch. This queue is a subset of the | |
| // mDiscoveredList.list of EFI_CORE_DRIVER_ENTRY. | |
| // | |
| LIST_ENTRY mScheduledQueue = INITIALIZE_LIST_HEAD_VARIABLE (mScheduledQueue); | |
| // | |
| // List of handles who's Fv's have been parsed and added to the mFwDriverList. | |
| // | |
| LIST_ENTRY mFvHandleList = INITIALIZE_LIST_HEAD_VARIABLE (mFvHandleList); // list of KNOWN_HANDLE | |
| // | |
| // Lock for mDiscoveredList, mScheduledQueue, gDispatcherRunning. | |
| // | |
| EFI_LOCK mDispatcherLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_HIGH_LEVEL); | |
| // | |
| // Flag for the DXE Dispacher. TRUE if dispatcher is execuing. | |
| // | |
| BOOLEAN gDispatcherRunning = FALSE; | |
| // | |
| // Module globals to manage the FwVol registration notification event | |
| // | |
| EFI_EVENT mFwVolEvent; | |
| VOID *mFwVolEventRegistration; | |
| // | |
| // List of file types supported by dispatcher | |
| // | |
| EFI_FV_FILETYPE mDxeFileTypes[] = { | |
| EFI_FV_FILETYPE_DRIVER, | |
| EFI_FV_FILETYPE_COMBINED_SMM_DXE, | |
| EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER, | |
| EFI_FV_FILETYPE_DXE_CORE, | |
| EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE | |
| }; | |
| typedef struct { | |
| MEDIA_FW_VOL_FILEPATH_DEVICE_PATH File; | |
| EFI_DEVICE_PATH_PROTOCOL End; | |
| } FV_FILEPATH_DEVICE_PATH; | |
| FV_FILEPATH_DEVICE_PATH mFvDevicePath; | |
| // | |
| // Function Prototypes | |
| // | |
| /** | |
| Insert InsertedDriverEntry onto the mScheduledQueue. To do this you | |
| must add any driver with a before dependency on InsertedDriverEntry first. | |
| You do this by recursively calling this routine. After all the Befores are | |
| processed you can add InsertedDriverEntry to the mScheduledQueue. | |
| Then you can add any driver with an After dependency on InsertedDriverEntry | |
| by recursively calling this routine. | |
| @param InsertedDriverEntry The driver to insert on the ScheduledLink Queue | |
| **/ | |
| VOID | |
| CoreInsertOnScheduledQueueWhileProcessingBeforeAndAfter ( | |
| IN EFI_CORE_DRIVER_ENTRY *InsertedDriverEntry | |
| ); | |
| /** | |
| Event notification that is fired every time a FV dispatch protocol is added. | |
| More than one protocol may have been added when this event is fired, so you | |
| must loop on CoreLocateHandle () to see how many protocols were added and | |
| do the following to each FV: | |
| If the Fv has already been processed, skip it. If the Fv has not been | |
| processed then mark it as being processed, as we are about to process it. | |
| Read the Fv and add any driver in the Fv to the mDiscoveredList.The | |
| mDiscoveredList is never free'ed and contains variables that define | |
| the other states the DXE driver transitions to.. | |
| While you are at it read the A Priori file into memory. | |
| Place drivers in the A Priori list onto the mScheduledQueue. | |
| @param Event The Event that is being processed, not used. | |
| @param Context Event Context, not used. | |
| **/ | |
| VOID | |
| EFIAPI | |
| CoreFwVolEventProtocolNotify ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ); | |
| /** | |
| Convert FvHandle and DriverName into an EFI device path | |
| @param Fv Fv protocol, needed to read Depex info out of | |
| FLASH. | |
| @param FvHandle Handle for Fv, needed in the | |
| EFI_CORE_DRIVER_ENTRY so that the PE image can be | |
| read out of the FV at a later time. | |
| @param DriverName Name of driver to add to mDiscoveredList. | |
| @return Pointer to device path constructed from FvHandle and DriverName | |
| **/ | |
| EFI_DEVICE_PATH_PROTOCOL * | |
| CoreFvToDevicePath ( | |
| IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv, | |
| IN EFI_HANDLE FvHandle, | |
| IN EFI_GUID *DriverName | |
| ); | |
| /** | |
| Add an entry to the mDiscoveredList. Allocate memory to store the DriverEntry, | |
| and initilize any state variables. Read the Depex from the FV and store it | |
| in DriverEntry. Pre-process the Depex to set the SOR, Before and After state. | |
| The Discovered list is never free'ed and contains booleans that represent the | |
| other possible DXE driver states. | |
| @param Fv Fv protocol, needed to read Depex info out of | |
| FLASH. | |
| @param FvHandle Handle for Fv, needed in the | |
| EFI_CORE_DRIVER_ENTRY so that the PE image can be | |
| read out of the FV at a later time. | |
| @param DriverName Name of driver to add to mDiscoveredList. | |
| @param Type Fv File Type of file to add to mDiscoveredList. | |
| @retval EFI_SUCCESS If driver was added to the mDiscoveredList. | |
| @retval EFI_ALREADY_STARTED The driver has already been started. Only one | |
| DriverName may be active in the system at any one | |
| time. | |
| **/ | |
| EFI_STATUS | |
| CoreAddToDriverList ( | |
| IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv, | |
| IN EFI_HANDLE FvHandle, | |
| IN EFI_GUID *DriverName, | |
| IN EFI_FV_FILETYPE Type | |
| ); | |
| /** | |
| Get Fv image(s) from the FV through file name, and produce FVB protocol for every Fv image(s). | |
| @param Fv The FIRMWARE_VOLUME protocol installed on the FV. | |
| @param FvHandle The handle which FVB protocol installed on. | |
| @param FileName The file name guid specified. | |
| @retval EFI_OUT_OF_RESOURCES No enough memory or other resource. | |
| @retval EFI_SUCCESS Function successfully returned. | |
| **/ | |
| EFI_STATUS | |
| CoreProcessFvImageFile ( | |
| IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv, | |
| IN EFI_HANDLE FvHandle, | |
| IN EFI_GUID *FileName | |
| ); | |
| /** | |
| Enter critical section by gaining lock on mDispatcherLock. | |
| **/ | |
| VOID | |
| CoreAcquireDispatcherLock ( | |
| VOID | |
| ) | |
| { | |
| CoreAcquireLock (&mDispatcherLock); | |
| } | |
| /** | |
| Exit critical section by releasing lock on mDispatcherLock. | |
| **/ | |
| VOID | |
| CoreReleaseDispatcherLock ( | |
| VOID | |
| ) | |
| { | |
| CoreReleaseLock (&mDispatcherLock); | |
| } | |
| /** | |
| Read Depex and pre-process the Depex for Before and After. If Section Extraction | |
| protocol returns an error via ReadSection defer the reading of the Depex. | |
| @param DriverEntry Driver to work on. | |
| @retval EFI_SUCCESS Depex read and preprossesed | |
| @retval EFI_PROTOCOL_ERROR The section extraction protocol returned an error | |
| and Depex reading needs to be retried. | |
| @retval Error DEPEX not found. | |
| **/ | |
| EFI_STATUS | |
| CoreGetDepexSectionAndPreProccess ( | |
| IN EFI_CORE_DRIVER_ENTRY *DriverEntry | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_SECTION_TYPE SectionType; | |
| UINT32 AuthenticationStatus; | |
| EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; | |
| Fv = DriverEntry->Fv; | |
| // | |
| // Grab Depex info, it will never be free'ed. | |
| // | |
| SectionType = EFI_SECTION_DXE_DEPEX; | |
| Status = Fv->ReadSection ( | |
| DriverEntry->Fv, | |
| &DriverEntry->FileName, | |
| SectionType, | |
| 0, | |
| &DriverEntry->Depex, | |
| (UINTN *)&DriverEntry->DepexSize, | |
| &AuthenticationStatus | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| if (Status == EFI_PROTOCOL_ERROR) { | |
| // | |
| // The section extraction protocol failed so set protocol error flag | |
| // | |
| DriverEntry->DepexProtocolError = TRUE; | |
| } else { | |
| // | |
| // If no Depex assume UEFI 2.0 driver model | |
| // | |
| DriverEntry->Depex = NULL; | |
| DriverEntry->Dependent = TRUE; | |
| DriverEntry->DepexProtocolError = FALSE; | |
| } | |
| } else { | |
| // | |
| // Set Before, After, and Unrequested state information based on Depex | |
| // Driver will be put in Dependent or Unrequested state | |
| // | |
| CorePreProcessDepex (DriverEntry); | |
| DriverEntry->DepexProtocolError = FALSE; | |
| } | |
| return Status; | |
| } | |
| /** | |
| Check every driver and locate a matching one. If the driver is found, the Unrequested | |
| state flag is cleared. | |
| @param FirmwareVolumeHandle The handle of the Firmware Volume that contains | |
| the firmware file specified by DriverName. | |
| @param DriverName The Driver name to put in the Dependent state. | |
| @retval EFI_SUCCESS The DriverName was found and it's SOR bit was | |
| cleared | |
| @retval EFI_NOT_FOUND The DriverName does not exist or it's SOR bit was | |
| not set. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| CoreSchedule ( | |
| IN EFI_HANDLE FirmwareVolumeHandle, | |
| IN EFI_GUID *DriverName | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| EFI_CORE_DRIVER_ENTRY *DriverEntry; | |
| // | |
| // Check every driver | |
| // | |
| for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { | |
| DriverEntry = CR (Link, EFI_CORE_DRIVER_ENTRY, Link, EFI_CORE_DRIVER_ENTRY_SIGNATURE); | |
| if ((DriverEntry->FvHandle == FirmwareVolumeHandle) && | |
| DriverEntry->Unrequested && | |
| CompareGuid (DriverName, &DriverEntry->FileName)) | |
| { | |
| // | |
| // Move the driver from the Unrequested to the Dependent state | |
| // | |
| CoreAcquireDispatcherLock (); | |
| DriverEntry->Unrequested = FALSE; | |
| DriverEntry->Dependent = TRUE; | |
| CoreReleaseDispatcherLock (); | |
| DEBUG ((DEBUG_DISPATCH, "Schedule FFS(%g) - EFI_SUCCESS\n", DriverName)); | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| DEBUG ((DEBUG_DISPATCH, "Schedule FFS(%g) - EFI_NOT_FOUND\n", DriverName)); | |
| return EFI_NOT_FOUND; | |
| } | |
| /** | |
| Convert a driver from the Untrused back to the Scheduled state. | |
| @param FirmwareVolumeHandle The handle of the Firmware Volume that contains | |
| the firmware file specified by DriverName. | |
| @param DriverName The Driver name to put in the Scheduled state | |
| @retval EFI_SUCCESS The file was found in the untrusted state, and it | |
| was promoted to the trusted state. | |
| @retval EFI_NOT_FOUND The file was not found in the untrusted state. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| CoreTrust ( | |
| IN EFI_HANDLE FirmwareVolumeHandle, | |
| IN EFI_GUID *DriverName | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| EFI_CORE_DRIVER_ENTRY *DriverEntry; | |
| // | |
| // Check every driver | |
| // | |
| for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { | |
| DriverEntry = CR (Link, EFI_CORE_DRIVER_ENTRY, Link, EFI_CORE_DRIVER_ENTRY_SIGNATURE); | |
| if ((DriverEntry->FvHandle == FirmwareVolumeHandle) && | |
| DriverEntry->Untrusted && | |
| CompareGuid (DriverName, &DriverEntry->FileName)) | |
| { | |
| // | |
| // Transition driver from Untrusted to Scheduled state. | |
| // | |
| CoreAcquireDispatcherLock (); | |
| DriverEntry->Untrusted = FALSE; | |
| DriverEntry->Scheduled = TRUE; | |
| InsertTailList (&mScheduledQueue, &DriverEntry->ScheduledLink); | |
| CoreReleaseDispatcherLock (); | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| return EFI_NOT_FOUND; | |
| } | |
| /** | |
| This is the main Dispatcher for DXE and it exits when there are no more | |
| drivers to run. Drain the mScheduledQueue and load and start a PE | |
| image for each driver. Search the mDiscoveredList to see if any driver can | |
| be placed on the mScheduledQueue. If no drivers are placed on the | |
| mScheduledQueue exit the function. On exit it is assumed the Bds() | |
| will be called, and when the Bds() exits the Dispatcher will be called | |
| again. | |
| @retval EFI_ALREADY_STARTED The DXE Dispatcher is already running | |
| @retval EFI_NOT_FOUND No DXE Drivers were dispatched | |
| @retval EFI_SUCCESS One or more DXE Drivers were dispatched | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| CoreDispatcher ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_STATUS ReturnStatus; | |
| LIST_ENTRY *Link; | |
| EFI_CORE_DRIVER_ENTRY *DriverEntry; | |
| BOOLEAN ReadyToRun; | |
| EFI_EVENT DxeDispatchEvent; | |
| PERF_FUNCTION_BEGIN (); | |
| if (gDispatcherRunning) { | |
| // | |
| // If the dispatcher is running don't let it be restarted. | |
| // | |
| return EFI_ALREADY_STARTED; | |
| } | |
| gDispatcherRunning = TRUE; | |
| Status = CoreCreateEventEx ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_NOTIFY, | |
| EfiEventEmptyFunction, | |
| NULL, | |
| &gEfiEventDxeDispatchGuid, | |
| &DxeDispatchEvent | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| ReturnStatus = EFI_NOT_FOUND; | |
| do { | |
| // | |
| // Drain the Scheduled Queue | |
| // | |
| while (!IsListEmpty (&mScheduledQueue)) { | |
| DriverEntry = CR ( | |
| mScheduledQueue.ForwardLink, | |
| EFI_CORE_DRIVER_ENTRY, | |
| ScheduledLink, | |
| EFI_CORE_DRIVER_ENTRY_SIGNATURE | |
| ); | |
| // | |
| // Load the DXE Driver image into memory. If the Driver was transitioned from | |
| // Untrused to Scheduled it would have already been loaded so we may need to | |
| // skip the LoadImage | |
| // | |
| if ((DriverEntry->ImageHandle == NULL) && !DriverEntry->IsFvImage) { | |
| DEBUG ((DEBUG_INFO, "Loading driver %g\n", &DriverEntry->FileName)); | |
| Status = CoreLoadImage ( | |
| FALSE, | |
| gDxeCoreImageHandle, | |
| DriverEntry->FvFileDevicePath, | |
| NULL, | |
| 0, | |
| &DriverEntry->ImageHandle | |
| ); | |
| // | |
| // Update the driver state to reflect that it's been loaded | |
| // | |
| if (EFI_ERROR (Status)) { | |
| CoreAcquireDispatcherLock (); | |
| if (Status == EFI_SECURITY_VIOLATION) { | |
| // | |
| // Take driver from Scheduled to Untrused state | |
| // | |
| DriverEntry->Untrusted = TRUE; | |
| } else { | |
| // | |
| // The DXE Driver could not be loaded, and do not attempt to load or start it again. | |
| // Take driver from Scheduled to Initialized. | |
| // | |
| // This case include the Never Trusted state if EFI_ACCESS_DENIED is returned | |
| // | |
| DriverEntry->Initialized = TRUE; | |
| } | |
| DriverEntry->Scheduled = FALSE; | |
| RemoveEntryList (&DriverEntry->ScheduledLink); | |
| CoreReleaseDispatcherLock (); | |
| // | |
| // If it's an error don't try the StartImage | |
| // | |
| continue; | |
| } | |
| } | |
| CoreAcquireDispatcherLock (); | |
| DriverEntry->Scheduled = FALSE; | |
| DriverEntry->Initialized = TRUE; | |
| RemoveEntryList (&DriverEntry->ScheduledLink); | |
| CoreReleaseDispatcherLock (); | |
| if (DriverEntry->IsFvImage) { | |
| // | |
| // Produce a firmware volume block protocol for FvImage so it gets dispatched from. | |
| // | |
| Status = CoreProcessFvImageFile (DriverEntry->Fv, DriverEntry->FvHandle, &DriverEntry->FileName); | |
| } else { | |
| REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( | |
| EFI_PROGRESS_CODE, | |
| (EFI_SOFTWARE_DXE_CORE | EFI_SW_PC_INIT_BEGIN), | |
| &DriverEntry->ImageHandle, | |
| sizeof (DriverEntry->ImageHandle) | |
| ); | |
| ASSERT (DriverEntry->ImageHandle != NULL); | |
| Status = CoreStartImage (DriverEntry->ImageHandle, NULL, NULL); | |
| REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( | |
| EFI_PROGRESS_CODE, | |
| (EFI_SOFTWARE_DXE_CORE | EFI_SW_PC_INIT_END), | |
| &DriverEntry->ImageHandle, | |
| sizeof (DriverEntry->ImageHandle) | |
| ); | |
| } | |
| ReturnStatus = EFI_SUCCESS; | |
| } | |
| // | |
| // Now DXE Dispatcher finished one round of dispatch, signal an event group | |
| // so that SMM Dispatcher get chance to dispatch SMM Drivers which depend | |
| // on UEFI protocols | |
| // | |
| if (!EFI_ERROR (ReturnStatus)) { | |
| CoreSignalEvent (DxeDispatchEvent); | |
| } | |
| // | |
| // Search DriverList for items to place on Scheduled Queue | |
| // | |
| ReadyToRun = FALSE; | |
| for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { | |
| DriverEntry = CR (Link, EFI_CORE_DRIVER_ENTRY, Link, EFI_CORE_DRIVER_ENTRY_SIGNATURE); | |
| if (DriverEntry->DepexProtocolError) { | |
| // | |
| // If Section Extraction Protocol did not let the Depex be read before retry the read | |
| // | |
| Status = CoreGetDepexSectionAndPreProccess (DriverEntry); | |
| } | |
| if (DriverEntry->Dependent) { | |
| if (CoreIsSchedulable (DriverEntry)) { | |
| CoreInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry); | |
| ReadyToRun = TRUE; | |
| } | |
| } else { | |
| if (DriverEntry->Unrequested) { | |
| DEBUG ((DEBUG_DISPATCH, "Evaluate DXE DEPEX for FFS(%g)\n", &DriverEntry->FileName)); | |
| DEBUG ((DEBUG_DISPATCH, " SOR = Not Requested\n")); | |
| DEBUG ((DEBUG_DISPATCH, " RESULT = FALSE\n")); | |
| } | |
| } | |
| } | |
| } while (ReadyToRun); | |
| // | |
| // Close DXE dispatch Event | |
| // | |
| CoreCloseEvent (DxeDispatchEvent); | |
| gDispatcherRunning = FALSE; | |
| PERF_FUNCTION_END (); | |
| return ReturnStatus; | |
| } | |
| /** | |
| Insert InsertedDriverEntry onto the mScheduledQueue. To do this you | |
| must add any driver with a before dependency on InsertedDriverEntry first. | |
| You do this by recursively calling this routine. After all the Befores are | |
| processed you can add InsertedDriverEntry to the mScheduledQueue. | |
| Then you can add any driver with an After dependency on InsertedDriverEntry | |
| by recursively calling this routine. | |
| @param InsertedDriverEntry The driver to insert on the ScheduledLink Queue | |
| **/ | |
| VOID | |
| CoreInsertOnScheduledQueueWhileProcessingBeforeAndAfter ( | |
| IN EFI_CORE_DRIVER_ENTRY *InsertedDriverEntry | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| EFI_CORE_DRIVER_ENTRY *DriverEntry; | |
| // | |
| // Process Before Dependency | |
| // | |
| for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { | |
| DriverEntry = CR (Link, EFI_CORE_DRIVER_ENTRY, Link, EFI_CORE_DRIVER_ENTRY_SIGNATURE); | |
| if (DriverEntry->Before && DriverEntry->Dependent && (DriverEntry != InsertedDriverEntry)) { | |
| DEBUG ((DEBUG_DISPATCH, "Evaluate DXE DEPEX for FFS(%g)\n", &DriverEntry->FileName)); | |
| DEBUG ((DEBUG_DISPATCH, " BEFORE FFS(%g) = ", &DriverEntry->BeforeAfterGuid)); | |
| if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) { | |
| // | |
| // Recursively process BEFORE | |
| // | |
| DEBUG ((DEBUG_DISPATCH, "TRUE\n END\n RESULT = TRUE\n")); | |
| CoreInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry); | |
| } else { | |
| DEBUG ((DEBUG_DISPATCH, "FALSE\n END\n RESULT = FALSE\n")); | |
| } | |
| } | |
| } | |
| // | |
| // Convert driver from Dependent to Scheduled state | |
| // | |
| CoreAcquireDispatcherLock (); | |
| InsertedDriverEntry->Dependent = FALSE; | |
| InsertedDriverEntry->Scheduled = TRUE; | |
| InsertTailList (&mScheduledQueue, &InsertedDriverEntry->ScheduledLink); | |
| CoreReleaseDispatcherLock (); | |
| // | |
| // Process After Dependency | |
| // | |
| for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { | |
| DriverEntry = CR (Link, EFI_CORE_DRIVER_ENTRY, Link, EFI_CORE_DRIVER_ENTRY_SIGNATURE); | |
| if (DriverEntry->After && DriverEntry->Dependent && (DriverEntry != InsertedDriverEntry)) { | |
| DEBUG ((DEBUG_DISPATCH, "Evaluate DXE DEPEX for FFS(%g)\n", &DriverEntry->FileName)); | |
| DEBUG ((DEBUG_DISPATCH, " AFTER FFS(%g) = ", &DriverEntry->BeforeAfterGuid)); | |
| if (CompareGuid (&InsertedDriverEntry->FileName, &DriverEntry->BeforeAfterGuid)) { | |
| // | |
| // Recursively process AFTER | |
| // | |
| DEBUG ((DEBUG_DISPATCH, "TRUE\n END\n RESULT = TRUE\n")); | |
| CoreInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry); | |
| } else { | |
| DEBUG ((DEBUG_DISPATCH, "FALSE\n END\n RESULT = FALSE\n")); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| Return TRUE if the Fv has been processed, FALSE if not. | |
| @param FvHandle The handle of a FV that's being tested | |
| @retval TRUE Fv protocol on FvHandle has been processed | |
| @retval FALSE Fv protocol on FvHandle has not yet been processed | |
| **/ | |
| BOOLEAN | |
| FvHasBeenProcessed ( | |
| IN EFI_HANDLE FvHandle | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| KNOWN_HANDLE *KnownHandle; | |
| for (Link = mFvHandleList.ForwardLink; Link != &mFvHandleList; Link = Link->ForwardLink) { | |
| KnownHandle = CR (Link, KNOWN_HANDLE, Link, KNOWN_HANDLE_SIGNATURE); | |
| if (KnownHandle->Handle == FvHandle) { | |
| return TRUE; | |
| } | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| Remember that Fv protocol on FvHandle has had it's drivers placed on the | |
| mDiscoveredList. This fucntion adds entries on the mFvHandleList if new | |
| entry is different from one in mFvHandleList by checking FvImage Guid. | |
| Items are never removed/freed from the mFvHandleList. | |
| @param FvHandle The handle of a FV that has been processed | |
| @return A point to new added FvHandle entry. If FvHandle with the same FvImage guid | |
| has been added, NULL will return. | |
| **/ | |
| KNOWN_HANDLE * | |
| FvIsBeingProcessed ( | |
| IN EFI_HANDLE FvHandle | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_GUID FvNameGuid; | |
| BOOLEAN FvNameGuidIsFound; | |
| UINT32 ExtHeaderOffset; | |
| EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; | |
| EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; | |
| EFI_FV_BLOCK_MAP_ENTRY *BlockMap; | |
| UINTN LbaOffset; | |
| UINTN Index; | |
| EFI_LBA LbaIndex; | |
| LIST_ENTRY *Link; | |
| KNOWN_HANDLE *KnownHandle; | |
| FwVolHeader = NULL; | |
| // | |
| // Get the FirmwareVolumeBlock protocol on that handle | |
| // | |
| FvNameGuidIsFound = FALSE; | |
| Status = CoreHandleProtocol (FvHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Get the full FV header based on FVB protocol. | |
| // | |
| ASSERT (Fvb != NULL); | |
| Status = GetFwVolHeader (Fvb, &FwVolHeader); | |
| if (!EFI_ERROR (Status)) { | |
| ASSERT (FwVolHeader != NULL); | |
| if (VerifyFvHeaderChecksum (FwVolHeader) && (FwVolHeader->ExtHeaderOffset != 0)) { | |
| ExtHeaderOffset = (UINT32)FwVolHeader->ExtHeaderOffset; | |
| BlockMap = FwVolHeader->BlockMap; | |
| LbaIndex = 0; | |
| LbaOffset = 0; | |
| // | |
| // Find LbaIndex and LbaOffset for FV extension header based on BlockMap. | |
| // | |
| while ((BlockMap->NumBlocks != 0) || (BlockMap->Length != 0)) { | |
| for (Index = 0; Index < BlockMap->NumBlocks && ExtHeaderOffset >= BlockMap->Length; Index++) { | |
| ExtHeaderOffset -= BlockMap->Length; | |
| LbaIndex++; | |
| } | |
| // | |
| // Check whether FvExtHeader is crossing the multi block range. | |
| // | |
| if (Index < BlockMap->NumBlocks) { | |
| LbaOffset = ExtHeaderOffset; | |
| break; | |
| } | |
| BlockMap++; | |
| } | |
| // | |
| // Read FvNameGuid from FV extension header. | |
| // | |
| Status = ReadFvbData (Fvb, &LbaIndex, &LbaOffset, sizeof (FvNameGuid), (UINT8 *)&FvNameGuid); | |
| if (!EFI_ERROR (Status)) { | |
| FvNameGuidIsFound = TRUE; | |
| } | |
| } | |
| CoreFreePool (FwVolHeader); | |
| } | |
| } | |
| if (FvNameGuidIsFound) { | |
| // | |
| // Check whether the FV image with the found FvNameGuid has been processed. | |
| // | |
| for (Link = mFvHandleList.ForwardLink; Link != &mFvHandleList; Link = Link->ForwardLink) { | |
| KnownHandle = CR (Link, KNOWN_HANDLE, Link, KNOWN_HANDLE_SIGNATURE); | |
| if (CompareGuid (&FvNameGuid, &KnownHandle->FvNameGuid)) { | |
| DEBUG ((DEBUG_ERROR, "FvImage on FvHandle %p and %p has the same FvNameGuid %g.\n", FvHandle, KnownHandle->Handle, &FvNameGuid)); | |
| return NULL; | |
| } | |
| } | |
| } | |
| KnownHandle = AllocateZeroPool (sizeof (KNOWN_HANDLE)); | |
| ASSERT (KnownHandle != NULL); | |
| KnownHandle->Signature = KNOWN_HANDLE_SIGNATURE; | |
| KnownHandle->Handle = FvHandle; | |
| if (FvNameGuidIsFound) { | |
| CopyGuid (&KnownHandle->FvNameGuid, &FvNameGuid); | |
| } | |
| InsertTailList (&mFvHandleList, &KnownHandle->Link); | |
| return KnownHandle; | |
| } | |
| /** | |
| Convert FvHandle and DriverName into an EFI device path | |
| @param Fv Fv protocol, needed to read Depex info out of | |
| FLASH. | |
| @param FvHandle Handle for Fv, needed in the | |
| EFI_CORE_DRIVER_ENTRY so that the PE image can be | |
| read out of the FV at a later time. | |
| @param DriverName Name of driver to add to mDiscoveredList. | |
| @return Pointer to device path constructed from FvHandle and DriverName | |
| **/ | |
| EFI_DEVICE_PATH_PROTOCOL * | |
| CoreFvToDevicePath ( | |
| IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv, | |
| IN EFI_HANDLE FvHandle, | |
| IN EFI_GUID *DriverName | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_DEVICE_PATH_PROTOCOL *FvDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *FileNameDevicePath; | |
| // | |
| // Remember the device path of the FV | |
| // | |
| Status = CoreHandleProtocol (FvHandle, &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath); | |
| if (EFI_ERROR (Status)) { | |
| FileNameDevicePath = NULL; | |
| } else { | |
| // | |
| // Build a device path to the file in the FV to pass into gBS->LoadImage | |
| // | |
| EfiInitializeFwVolDevicepathNode (&mFvDevicePath.File, DriverName); | |
| SetDevicePathEndNode (&mFvDevicePath.End); | |
| FileNameDevicePath = AppendDevicePath ( | |
| FvDevicePath, | |
| (EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath | |
| ); | |
| } | |
| return FileNameDevicePath; | |
| } | |
| /** | |
| Add an entry to the mDiscoveredList. Allocate memory to store the DriverEntry, | |
| and initilize any state variables. Read the Depex from the FV and store it | |
| in DriverEntry. Pre-process the Depex to set the SOR, Before and After state. | |
| The Discovered list is never free'ed and contains booleans that represent the | |
| other possible DXE driver states. | |
| @param Fv Fv protocol, needed to read Depex info out of | |
| FLASH. | |
| @param FvHandle Handle for Fv, needed in the | |
| EFI_CORE_DRIVER_ENTRY so that the PE image can be | |
| read out of the FV at a later time. | |
| @param DriverName Name of driver to add to mDiscoveredList. | |
| @param Type Fv File Type of file to add to mDiscoveredList. | |
| @retval EFI_SUCCESS If driver was added to the mDiscoveredList. | |
| @retval EFI_ALREADY_STARTED The driver has already been started. Only one | |
| DriverName may be active in the system at any one | |
| time. | |
| **/ | |
| EFI_STATUS | |
| CoreAddToDriverList ( | |
| IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv, | |
| IN EFI_HANDLE FvHandle, | |
| IN EFI_GUID *DriverName, | |
| IN EFI_FV_FILETYPE Type | |
| ) | |
| { | |
| EFI_CORE_DRIVER_ENTRY *DriverEntry; | |
| // | |
| // Create the Driver Entry for the list. ZeroPool initializes lots of variables to | |
| // NULL or FALSE. | |
| // | |
| DriverEntry = AllocateZeroPool (sizeof (EFI_CORE_DRIVER_ENTRY)); | |
| ASSERT (DriverEntry != NULL); | |
| if (Type == EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE) { | |
| DriverEntry->IsFvImage = TRUE; | |
| } | |
| DriverEntry->Signature = EFI_CORE_DRIVER_ENTRY_SIGNATURE; | |
| CopyGuid (&DriverEntry->FileName, DriverName); | |
| DriverEntry->FvHandle = FvHandle; | |
| DriverEntry->Fv = Fv; | |
| DriverEntry->FvFileDevicePath = CoreFvToDevicePath (Fv, FvHandle, DriverName); | |
| CoreGetDepexSectionAndPreProccess (DriverEntry); | |
| CoreAcquireDispatcherLock (); | |
| InsertTailList (&mDiscoveredList, &DriverEntry->Link); | |
| CoreReleaseDispatcherLock (); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check if a FV Image type file (EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE) is | |
| described by a EFI_HOB_FIRMWARE_VOLUME2 Hob. | |
| @param FvNameGuid The FV image guid specified. | |
| @param DriverName The driver guid specified. | |
| @retval TRUE This file is found in a EFI_HOB_FIRMWARE_VOLUME2 | |
| Hob. | |
| @retval FALSE Not found. | |
| **/ | |
| BOOLEAN | |
| FvFoundInHobFv2 ( | |
| IN CONST EFI_GUID *FvNameGuid, | |
| IN CONST EFI_GUID *DriverName | |
| ) | |
| { | |
| EFI_PEI_HOB_POINTERS HobFv2; | |
| HobFv2.Raw = GetHobList (); | |
| while ((HobFv2.Raw = GetNextHob (EFI_HOB_TYPE_FV2, HobFv2.Raw)) != NULL) { | |
| // | |
| // Compare parent FvNameGuid and FileGuid both. | |
| // | |
| if (CompareGuid (DriverName, &HobFv2.FirmwareVolume2->FileName) && | |
| CompareGuid (FvNameGuid, &HobFv2.FirmwareVolume2->FvName)) | |
| { | |
| return TRUE; | |
| } | |
| HobFv2.Raw = GET_NEXT_HOB (HobFv2); | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| Find USED_SIZE FV_EXT_TYPE entry in FV extension header and get the FV used size. | |
| @param[in] FvHeader Pointer to FV header. | |
| @param[out] FvUsedSize Pointer to FV used size returned, | |
| only valid if USED_SIZE FV_EXT_TYPE entry is found. | |
| @param[out] EraseByte Pointer to erase byte returned, | |
| only valid if USED_SIZE FV_EXT_TYPE entry is found. | |
| @retval TRUE USED_SIZE FV_EXT_TYPE entry is found, | |
| FV used size and erase byte are returned. | |
| @retval FALSE No USED_SIZE FV_EXT_TYPE entry found. | |
| **/ | |
| BOOLEAN | |
| GetFvUsedSize ( | |
| IN EFI_FIRMWARE_VOLUME_HEADER *FvHeader, | |
| OUT UINT32 *FvUsedSize, | |
| OUT UINT8 *EraseByte | |
| ) | |
| { | |
| UINT16 ExtHeaderOffset; | |
| EFI_FIRMWARE_VOLUME_EXT_HEADER *ExtHeader; | |
| EFI_FIRMWARE_VOLUME_EXT_ENTRY *ExtEntryList; | |
| EFI_FIRMWARE_VOLUME_EXT_ENTRY_USED_SIZE_TYPE *ExtEntryUsedSize; | |
| ExtHeaderOffset = ReadUnaligned16 (&FvHeader->ExtHeaderOffset); | |
| if (ExtHeaderOffset != 0) { | |
| ExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)((UINT8 *)FvHeader + ExtHeaderOffset); | |
| ExtEntryList = (EFI_FIRMWARE_VOLUME_EXT_ENTRY *)(ExtHeader + 1); | |
| while ((UINTN)ExtEntryList < ((UINTN)ExtHeader + ReadUnaligned32 (&ExtHeader->ExtHeaderSize))) { | |
| if (ReadUnaligned16 (&ExtEntryList->ExtEntryType) == EFI_FV_EXT_TYPE_USED_SIZE_TYPE) { | |
| // | |
| // USED_SIZE FV_EXT_TYPE entry is found. | |
| // | |
| ExtEntryUsedSize = (EFI_FIRMWARE_VOLUME_EXT_ENTRY_USED_SIZE_TYPE *)ExtEntryList; | |
| *FvUsedSize = ReadUnaligned32 (&ExtEntryUsedSize->UsedSize); | |
| if ((ReadUnaligned32 (&FvHeader->Attributes) & EFI_FVB2_ERASE_POLARITY) != 0) { | |
| *EraseByte = 0xFF; | |
| } else { | |
| *EraseByte = 0; | |
| } | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "FV at 0x%x has 0x%x used size, and erase byte is 0x%02x\n", | |
| FvHeader, | |
| *FvUsedSize, | |
| *EraseByte | |
| )); | |
| return TRUE; | |
| } | |
| ExtEntryList = (EFI_FIRMWARE_VOLUME_EXT_ENTRY *) | |
| ((UINT8 *)ExtEntryList + ReadUnaligned16 (&ExtEntryList->ExtEntrySize)); | |
| } | |
| } | |
| // | |
| // No USED_SIZE FV_EXT_TYPE entry found. | |
| // | |
| return FALSE; | |
| } | |
| /** | |
| Get Fv image(s) from the FV through file name, and produce FVB protocol for every Fv image(s). | |
| @param Fv The FIRMWARE_VOLUME protocol installed on the FV. | |
| @param FvHandle The handle which FVB protocol installed on. | |
| @param FileName The file name guid specified. | |
| @retval EFI_OUT_OF_RESOURCES No enough memory or other resource. | |
| @retval EFI_SUCCESS Function successfully returned. | |
| **/ | |
| EFI_STATUS | |
| CoreProcessFvImageFile ( | |
| IN EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv, | |
| IN EFI_HANDLE FvHandle, | |
| IN EFI_GUID *FileName | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_SECTION_TYPE SectionType; | |
| UINT32 AuthenticationStatus; | |
| VOID *Buffer; | |
| VOID *AlignedBuffer; | |
| UINTN BufferSize; | |
| EFI_FIRMWARE_VOLUME_HEADER *FvHeader; | |
| UINT32 FvAlignment; | |
| EFI_DEVICE_PATH_PROTOCOL *FvFileDevicePath; | |
| UINT32 FvUsedSize; | |
| UINT8 EraseByte; | |
| UINTN Index; | |
| // | |
| // Read firmware volume section(s) | |
| // | |
| SectionType = EFI_SECTION_FIRMWARE_VOLUME_IMAGE; | |
| Index = 0; | |
| do { | |
| FvHeader = NULL; | |
| FvAlignment = 0; | |
| Buffer = NULL; | |
| BufferSize = 0; | |
| AlignedBuffer = NULL; | |
| Status = Fv->ReadSection ( | |
| Fv, | |
| FileName, | |
| SectionType, | |
| Index, | |
| &Buffer, | |
| &BufferSize, | |
| &AuthenticationStatus | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Evaluate the authentication status of the Firmware Volume through | |
| // Security Architectural Protocol | |
| // | |
| if (gSecurity != NULL) { | |
| FvFileDevicePath = CoreFvToDevicePath (Fv, FvHandle, FileName); | |
| Status = gSecurity->FileAuthenticationState ( | |
| gSecurity, | |
| AuthenticationStatus, | |
| FvFileDevicePath | |
| ); | |
| if (FvFileDevicePath != NULL) { | |
| FreePool (FvFileDevicePath); | |
| } | |
| if (Status != EFI_SUCCESS) { | |
| // | |
| // Security check failed. The firmware volume should not be used for any purpose. | |
| // | |
| if (Buffer != NULL) { | |
| FreePool (Buffer); | |
| } | |
| break; | |
| } | |
| } | |
| // | |
| // FvImage should be at its required alignment. | |
| // | |
| FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)Buffer; | |
| // | |
| // If EFI_FVB2_WEAK_ALIGNMENT is set in the volume header then the first byte of the volume | |
| // can be aligned on any power-of-two boundary. A weakly aligned volume can not be moved from | |
| // its initial linked location and maintain its alignment. | |
| // | |
| if ((ReadUnaligned32 (&FvHeader->Attributes) & EFI_FVB2_WEAK_ALIGNMENT) != EFI_FVB2_WEAK_ALIGNMENT) { | |
| // | |
| // Get FvHeader alignment | |
| // | |
| FvAlignment = 1 << ((ReadUnaligned32 (&FvHeader->Attributes) & EFI_FVB2_ALIGNMENT) >> 16); | |
| // | |
| // FvAlignment must be greater than or equal to 8 bytes of the minimum FFS alignment value. | |
| // | |
| if (FvAlignment < 8) { | |
| FvAlignment = 8; | |
| } | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a() FV at 0x%x, FvAlignment required is 0x%x\n", | |
| __func__, | |
| FvHeader, | |
| FvAlignment | |
| )); | |
| // | |
| // Check FvImage alignment. | |
| // | |
| if ((UINTN)FvHeader % FvAlignment != 0) { | |
| // | |
| // Allocate the aligned buffer for the FvImage. | |
| // | |
| AlignedBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES (BufferSize), (UINTN)FvAlignment); | |
| if (AlignedBuffer == NULL) { | |
| FreePool (Buffer); | |
| Status = EFI_OUT_OF_RESOURCES; | |
| break; | |
| } else { | |
| // | |
| // Move FvImage into the aligned buffer and release the original buffer. | |
| // | |
| if (GetFvUsedSize (FvHeader, &FvUsedSize, &EraseByte)) { | |
| // | |
| // Copy the used bytes and fill the rest with the erase value. | |
| // | |
| CopyMem (AlignedBuffer, FvHeader, (UINTN)FvUsedSize); | |
| SetMem ( | |
| (UINT8 *)AlignedBuffer + FvUsedSize, | |
| (UINTN)(BufferSize - FvUsedSize), | |
| EraseByte | |
| ); | |
| } else { | |
| CopyMem (AlignedBuffer, Buffer, BufferSize); | |
| } | |
| FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)AlignedBuffer; | |
| FreePool (Buffer); | |
| Buffer = NULL; | |
| } | |
| } | |
| } | |
| // | |
| // Produce a FVB protocol for the file | |
| // | |
| Status = ProduceFVBProtocolOnBuffer ( | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)FvHeader, | |
| (UINT64)BufferSize, | |
| FvHandle, | |
| AuthenticationStatus, | |
| NULL | |
| ); | |
| } | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // ReadSection or Produce FVB failed, Free data buffer | |
| // | |
| if (Buffer != NULL) { | |
| FreePool (Buffer); | |
| } | |
| if (AlignedBuffer != NULL) { | |
| FreeAlignedPages (AlignedBuffer, EFI_SIZE_TO_PAGES (BufferSize)); | |
| } | |
| break; | |
| } else { | |
| Index++; | |
| } | |
| } while (TRUE); | |
| if (Index > 0) { | |
| // | |
| // At least one FvImage has been processed successfully. | |
| // | |
| return EFI_SUCCESS; | |
| } else { | |
| return Status; | |
| } | |
| } | |
| /** | |
| Event notification that is fired every time a FV dispatch protocol is added. | |
| More than one protocol may have been added when this event is fired, so you | |
| must loop on CoreLocateHandle () to see how many protocols were added and | |
| do the following to each FV: | |
| If the Fv has already been processed, skip it. If the Fv has not been | |
| processed then mark it as being processed, as we are about to process it. | |
| Read the Fv and add any driver in the Fv to the mDiscoveredList.The | |
| mDiscoveredList is never free'ed and contains variables that define | |
| the other states the DXE driver transitions to.. | |
| While you are at it read the A Priori file into memory. | |
| Place drivers in the A Priori list onto the mScheduledQueue. | |
| @param Event The Event that is being processed, not used. | |
| @param Context Event Context, not used. | |
| **/ | |
| VOID | |
| EFIAPI | |
| CoreFwVolEventProtocolNotify ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_STATUS GetNextFileStatus; | |
| EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; | |
| EFI_DEVICE_PATH_PROTOCOL *FvDevicePath; | |
| EFI_HANDLE FvHandle; | |
| UINTN BufferSize; | |
| EFI_GUID NameGuid; | |
| UINTN Key; | |
| EFI_FV_FILETYPE Type; | |
| EFI_FV_FILE_ATTRIBUTES Attributes; | |
| UINTN Size; | |
| EFI_CORE_DRIVER_ENTRY *DriverEntry; | |
| EFI_GUID *AprioriFile; | |
| UINTN AprioriEntryCount; | |
| UINTN Index; | |
| LIST_ENTRY *Link; | |
| UINT32 AuthenticationStatus; | |
| UINTN SizeOfBuffer; | |
| VOID *DepexBuffer; | |
| KNOWN_HANDLE *KnownHandle; | |
| FvHandle = NULL; | |
| while (TRUE) { | |
| BufferSize = sizeof (EFI_HANDLE); | |
| Status = CoreLocateHandle ( | |
| ByRegisterNotify, | |
| NULL, | |
| mFwVolEventRegistration, | |
| &BufferSize, | |
| &FvHandle | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // If no more notification events exit | |
| // | |
| return; | |
| } | |
| if (FvHasBeenProcessed (FvHandle)) { | |
| // | |
| // This Fv has already been processed so lets skip it! | |
| // | |
| continue; | |
| } | |
| // | |
| // Since we are about to process this Fv mark it as processed. | |
| // | |
| KnownHandle = FvIsBeingProcessed (FvHandle); | |
| if (KnownHandle == NULL) { | |
| // | |
| // The FV with the same FV name guid has already been processed. | |
| // So lets skip it! | |
| // | |
| continue; | |
| } | |
| Status = CoreHandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv); | |
| if (EFI_ERROR (Status) || (Fv == NULL)) { | |
| // | |
| // FvHandle must have Firmware Volume2 protocol thus we should never get here. | |
| // | |
| ASSERT (FALSE); | |
| continue; | |
| } | |
| Status = CoreHandleProtocol (FvHandle, &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // The Firmware volume doesn't have device path, can't be dispatched. | |
| // | |
| continue; | |
| } | |
| // | |
| // Discover Drivers in FV and add them to the Discovered Driver List. | |
| // Process EFI_FV_FILETYPE_DRIVER type and then EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER | |
| // EFI_FV_FILETYPE_DXE_CORE is processed to produce a Loaded Image protocol for the core | |
| // EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE is processed to create a Fvb | |
| // | |
| for (Index = 0; Index < sizeof (mDxeFileTypes) / sizeof (EFI_FV_FILETYPE); Index++) { | |
| // | |
| // Initialize the search key | |
| // | |
| Key = 0; | |
| do { | |
| Type = mDxeFileTypes[Index]; | |
| GetNextFileStatus = Fv->GetNextFile ( | |
| Fv, | |
| &Key, | |
| &Type, | |
| &NameGuid, | |
| &Attributes, | |
| &Size | |
| ); | |
| if (!EFI_ERROR (GetNextFileStatus)) { | |
| if (Type == EFI_FV_FILETYPE_DXE_CORE) { | |
| // | |
| // If this is the DXE core fill in it's DevicePath & DeviceHandle | |
| // | |
| if (gDxeCoreLoadedImage->FilePath == NULL) { | |
| if (CompareGuid (&NameGuid, gDxeCoreFileName)) { | |
| // | |
| // Maybe One specail Fv cantains only one DXE_CORE module, so its device path must | |
| // be initialized completely. | |
| // | |
| EfiInitializeFwVolDevicepathNode (&mFvDevicePath.File, &NameGuid); | |
| SetDevicePathEndNode (&mFvDevicePath.End); | |
| gDxeCoreLoadedImage->FilePath = DuplicateDevicePath ( | |
| (EFI_DEVICE_PATH_PROTOCOL *)&mFvDevicePath | |
| ); | |
| gDxeCoreLoadedImage->DeviceHandle = FvHandle; | |
| } | |
| } | |
| } else if (Type == EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE) { | |
| // | |
| // Check if this EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE file has already | |
| // been extracted. | |
| // | |
| if (FvFoundInHobFv2 (&KnownHandle->FvNameGuid, &NameGuid)) { | |
| continue; | |
| } | |
| // | |
| // Check if this EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE file has SMM depex section. | |
| // | |
| DepexBuffer = NULL; | |
| SizeOfBuffer = 0; | |
| Status = Fv->ReadSection ( | |
| Fv, | |
| &NameGuid, | |
| EFI_SECTION_SMM_DEPEX, | |
| 0, | |
| &DepexBuffer, | |
| &SizeOfBuffer, | |
| &AuthenticationStatus | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // If SMM depex section is found, this FV image is invalid to be supported. | |
| // ASSERT FALSE to report this FV image. | |
| // | |
| FreePool (DepexBuffer); | |
| ASSERT (FALSE); | |
| } | |
| // | |
| // Check if this EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE file has DXE depex section. | |
| // | |
| DepexBuffer = NULL; | |
| SizeOfBuffer = 0; | |
| Status = Fv->ReadSection ( | |
| Fv, | |
| &NameGuid, | |
| EFI_SECTION_DXE_DEPEX, | |
| 0, | |
| &DepexBuffer, | |
| &SizeOfBuffer, | |
| &AuthenticationStatus | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // If no depex section, produce a firmware volume block protocol for it so it gets dispatched from. | |
| // | |
| CoreProcessFvImageFile (Fv, FvHandle, &NameGuid); | |
| } else { | |
| // | |
| // If depex section is found, this FV image will be dispatched until its depex is evaluated to TRUE. | |
| // | |
| FreePool (DepexBuffer); | |
| CoreAddToDriverList (Fv, FvHandle, &NameGuid, Type); | |
| } | |
| } else { | |
| // | |
| // Transition driver from Undiscovered to Discovered state | |
| // | |
| CoreAddToDriverList (Fv, FvHandle, &NameGuid, Type); | |
| } | |
| } | |
| } while (!EFI_ERROR (GetNextFileStatus)); | |
| } | |
| // | |
| // Read the array of GUIDs from the Apriori file if it is present in the firmware volume | |
| // | |
| AprioriFile = NULL; | |
| Status = Fv->ReadSection ( | |
| Fv, | |
| &gAprioriGuid, | |
| EFI_SECTION_RAW, | |
| 0, | |
| (VOID **)&AprioriFile, | |
| &SizeOfBuffer, | |
| &AuthenticationStatus | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| AprioriEntryCount = SizeOfBuffer / sizeof (EFI_GUID); | |
| } else { | |
| AprioriEntryCount = 0; | |
| } | |
| // | |
| // Put drivers on Apriori List on the Scheduled queue. The Discovered List includes | |
| // drivers not in the current FV and these must be skipped since the a priori list | |
| // is only valid for the FV that it resided in. | |
| // | |
| for (Index = 0; Index < AprioriEntryCount; Index++) { | |
| for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { | |
| DriverEntry = CR (Link, EFI_CORE_DRIVER_ENTRY, Link, EFI_CORE_DRIVER_ENTRY_SIGNATURE); | |
| if (CompareGuid (&DriverEntry->FileName, &AprioriFile[Index]) && | |
| (FvHandle == DriverEntry->FvHandle)) | |
| { | |
| CoreAcquireDispatcherLock (); | |
| DriverEntry->Dependent = FALSE; | |
| DriverEntry->Scheduled = TRUE; | |
| InsertTailList (&mScheduledQueue, &DriverEntry->ScheduledLink); | |
| CoreReleaseDispatcherLock (); | |
| DEBUG ((DEBUG_DISPATCH, "Evaluate DXE DEPEX for FFS(%g)\n", &DriverEntry->FileName)); | |
| DEBUG ((DEBUG_DISPATCH, " RESULT = TRUE (Apriori)\n")); | |
| break; | |
| } | |
| } | |
| } | |
| // | |
| // Free data allocated by Fv->ReadSection () | |
| // | |
| CoreFreePool (AprioriFile); | |
| } | |
| } | |
| /** | |
| Initialize the dispatcher. Initialize the notification function that runs when | |
| an FV2 protocol is added to the system. | |
| **/ | |
| VOID | |
| CoreInitializeDispatcher ( | |
| VOID | |
| ) | |
| { | |
| PERF_FUNCTION_BEGIN (); | |
| mFwVolEvent = EfiCreateProtocolNotifyEvent ( | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| TPL_CALLBACK, | |
| CoreFwVolEventProtocolNotify, | |
| NULL, | |
| &mFwVolEventRegistration | |
| ); | |
| PERF_FUNCTION_END (); | |
| } | |
| // | |
| // Function only used in debug builds | |
| // | |
| /** | |
| Traverse the discovered list for any drivers that were discovered but not loaded | |
| because the dependency experessions evaluated to false. | |
| **/ | |
| VOID | |
| CoreDisplayDiscoveredNotDispatched ( | |
| VOID | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| EFI_CORE_DRIVER_ENTRY *DriverEntry; | |
| for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) { | |
| DriverEntry = CR (Link, EFI_CORE_DRIVER_ENTRY, Link, EFI_CORE_DRIVER_ENTRY_SIGNATURE); | |
| if (DriverEntry->Dependent) { | |
| DEBUG ((DEBUG_LOAD, "Driver %g was discovered but not loaded!!\n", &DriverEntry->FileName)); | |
| } | |
| } | |
| } |