5.2.4 LoadImage() and StartImage()

Use LoadImage()to load and relocate a UEFI Image into system memory, and prepare it for execution. Use StartImage()to transfer control to a UEFI Image that was previously loaded into system memory using LoadImage(). These services are typically used by the UEFI Boot Manager when processing load options for UEFI Drivers, UEFI Applications, or UEFI OS Loaders. UEFI drivers do not typically need to load other UEFI Drivers and/or UEFI applications.

One exception is a bus driver for a bus type that provides a storage container for UEFI Drivers and/or UEFI Applications. A PCI Option ROM is an example of a container with those attributes. A PCI Bus Driver is required to discover any PCI Option ROM containers present on PCI Adapters. If a PCI Option ROM contains one or more UEFI Drivers that are compatible with the currently executing CPU, then the PCI Bus Driver is required to load and start those UEFI Drivers using the LoadImage() and StartImage() services. The EDK II PCI Bus Driver that performs this operation can be found in MdeModulePkg/Bus/Pci/PciBusDxe.

Another exception is a UEFI Driver that needs to execute a UEFI Application for the purposes of extended diagnostics or to augment driver configuration. There are UEFI standard methods for a UEFI Driver to provide diagnostics and configuration through the use of the EFI_DRIVER_DIAGNOSTICS2_PROTOCOL and HII. If for some reason, a UEFI Driver requires diagnostics or configuration capabilities that cannot be expressed using these standard methods, a UEFI Driver could choose to execute a UEFI Application that provides those capabilities. In the case of a PCI Adapter, UEFI Applications could be stored in the PCI Option ROM container. The UEFI Driver would use the LoadImage() and StartImage() services to load and execute those UEFI Applications from that container.

The following code fragment in Example 64 shows an example of a UEFI Driver for a PCI controller that uses the LoadImage() and StartImage() service to load and execute a 32 KB UEFI Application that is stored 32 KB into the PCI Option ROM container associated with the PCI controller. PciControllerHandle is the EFI_HANDLE for the PCI Controller.

This example retrieves both the PCI I/O Protocol and the Device Path Protocol associated with PciControllerHandle. The Device Path Protocol is used to construct a proper device path for the UEFI Application stored in the PCI option ROM. Helper functions from the EDK II library DevicePathLib are used to fill in the contents of a new device path node for the UEFI Application stored in the PCI Option ROM and to append that device path node to the device path of the PCI controller. Use the PCI I/O Protocol to access the shadowed copy of the PCI Option ROM contents through the RomImage field. The shadowed copy of the PCI Option ROM was created when the PCI bus was enumerated and the PCI I/O Protocols were produced.

Note: The use of a 32 KB offset and 32 KB length simplifies this example. An addin adapter that stores UEFI Applications in a PCI Option ROM container would likely define vendor specific descriptors to determine the offset and size of one or more UEFI Applications.

Example 64-Load and Start a UEFI Application from a PCI Option ROM

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DevicePathLib.h>
#include <Protocol/DevicePath.h>
#include <Protocol/PciIo.h>
EFI_STATUS Status;
EFI_HANDLE PciControllerHandle;
EFI_PCI_IO_PROTOCOL *PciIo;
EFI_DEVICE_PATH_PROTOCOL *PciDevicePath;
MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH OptionRomNode;
EFI_DEVICE_PATH_PROTOCOL *PciOptionRomDevicePath;
EFI_HANDLE NewImageHandle;
//
// Retrieve PCI I/O Protocol associated with PciControllerHandle
//
Status = gBS->OpenProtocol (
PciControllerHandle,
&gEfiPciIoProtocolGuid,
(VOID **)&PciIo,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Retrieve Device Path Protocol associated with PciControllerHandle
//
Status = gBS->OpenProtocol (
PciControllerHandle,
&gEfiDevicePathProtocolGuid,
(VOID **)&PciDevicePath,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Create Device Path Protocol to UEFI Application in PCI Option ROM
//
OptionRomNode.Header.Type = MEDIA_DEVICE_PATH;
OptionRomNode.Header.SubType = MEDIA_RELATIVE_OFFSET_RANGE_DP;
SetDevicePathNodeLength (&OptionRomNode.Header, sizeof (OptionRomNode));
OptionRomNode.StartingOffset = BASE_32KB;
OptionRomNode.EndingOffset = BASE_64KB - 1;
PciOptionRomDevicePath = AppendDevicePathNode (
PciDevicePath,
&OptionRomNode.Header
);
//
// Load UEFI Image from PCI Option ROM container
//
Status = gBS->LoadImage (
FALSE,
gImageHandle,
PciOptionRomDevicePath,
(UINT8 *)(PciIo->RomImage) + SIZE_32KB,
SIZE_32KB,
&NewImageHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Start UEFI Image from PCI Option ROM container
//
Status = gBS->StartImage (NewImageHandle, NULL, NULL);
if (EFI_ERROR (Status)) {
return Status;
}