mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-24 11:45:27 +01:00
1489 lines
49 KiB
C
1489 lines
49 KiB
C
|
/** @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 *
|
||
|
FvIsBeingProcesssed (
|
||
|
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 ((EFI_D_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",
|
||
|
__FUNCTION__,
|
||
|
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 = FvIsBeingProcesssed (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));
|
||
|
}
|
||
|
}
|
||
|
}
|