summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.c764
1 files changed, 764 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.c
new file mode 100644
index 00000000..3dbc1272
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.c
@@ -0,0 +1,764 @@
+/** @file
+ PCI Rom supporting funtions implementation for PCI Bus module.
+
+Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "PciBus.h"
+
+/**
+ Load the EFI Image from Option ROM
+
+ @param PciIoDevice PCI IO device instance.
+ @param FilePath The file path of the EFI Image
+ @param BufferSize On input the size of Buffer in bytes. On output with a return
+ code of EFI_SUCCESS, the amount of data transferred to Buffer.
+ On output with a return code of EFI_BUFFER_TOO_SMALL,
+ the size of Buffer required to retrieve the requested file.
+ @param Buffer The memory buffer to transfer the file to. If Buffer is NULL,
+ then no the size of the requested file is returned in BufferSize.
+
+ @retval EFI_SUCCESS The file was loaded.
+ @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
+ BufferSize is NULL.
+ @retval EFI_NOT_FOUND Not found PCI Option Rom on PCI device.
+ @retval EFI_DEVICE_ERROR Failed to decompress PCI Option Rom image.
+ @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
+ BufferSize has been updated with the size needed to complete the request.
+**/
+EFI_STATUS
+LocalLoadFile2 (
+ IN PCI_IO_DEVICE *PciIoDevice,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH *EfiOpRomImageNode;
+ EFI_PCI_EXPANSION_ROM_HEADER *EfiRomHeader;
+ PCI_DATA_STRUCTURE *Pcir;
+ UINT32 ImageSize;
+ UINT8 *ImageBuffer;
+ UINT32 ImageLength;
+ UINT32 DestinationSize;
+ UINT32 ScratchSize;
+ VOID *Scratch;
+ EFI_DECOMPRESS_PROTOCOL *Decompress;
+ UINT32 InitializationSize;
+
+ EfiOpRomImageNode = (MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH *) FilePath;
+ if ((EfiOpRomImageNode == NULL) ||
+ (DevicePathType (FilePath) != MEDIA_DEVICE_PATH) ||
+ (DevicePathSubType (FilePath) != MEDIA_RELATIVE_OFFSET_RANGE_DP) ||
+ (DevicePathNodeLength (FilePath) != sizeof (MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH)) ||
+ (!IsDevicePathEnd (NextDevicePathNode (FilePath))) ||
+ (EfiOpRomImageNode->StartingOffset > EfiOpRomImageNode->EndingOffset) ||
+ (EfiOpRomImageNode->EndingOffset >= PciIoDevice->RomSize) ||
+ (BufferSize == NULL)
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EfiRomHeader = (EFI_PCI_EXPANSION_ROM_HEADER *) (
+ (UINT8 *) PciIoDevice->PciIo.RomImage + EfiOpRomImageNode->StartingOffset
+ );
+ if (EfiRomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
+ return EFI_NOT_FOUND;
+ }
+
+
+ Pcir = (PCI_DATA_STRUCTURE *) ((UINT8 *) EfiRomHeader + EfiRomHeader->PcirOffset);
+ ASSERT (Pcir->Signature == PCI_DATA_STRUCTURE_SIGNATURE);
+
+ if ((Pcir->CodeType == PCI_CODE_TYPE_EFI_IMAGE) &&
+ (EfiRomHeader->EfiSignature == EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE) &&
+ ((EfiRomHeader->EfiSubsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) ||
+ (EfiRomHeader->EfiSubsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER)) &&
+ (EfiRomHeader->CompressionType <= EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED)
+ ) {
+
+ ImageSize = Pcir->ImageLength * 512;
+ InitializationSize = (UINT32) EfiRomHeader->InitializationSize * 512;
+ if (InitializationSize > ImageSize || EfiRomHeader->EfiImageHeaderOffset >= InitializationSize) {
+ return EFI_NOT_FOUND;
+ }
+
+ ImageBuffer = (UINT8 *) EfiRomHeader + EfiRomHeader->EfiImageHeaderOffset;
+ ImageLength = InitializationSize - EfiRomHeader->EfiImageHeaderOffset;
+
+ if (EfiRomHeader->CompressionType != EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED) {
+ //
+ // Uncompressed: Copy the EFI Image directly to user's buffer
+ //
+ if (Buffer == NULL || *BufferSize < ImageLength) {
+ *BufferSize = ImageLength;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *BufferSize = ImageLength;
+ CopyMem (Buffer, ImageBuffer, ImageLength);
+ return EFI_SUCCESS;
+
+ } else {
+ //
+ // Compressed: Uncompress before copying
+ //
+ Status = gBS->LocateProtocol (&gEfiDecompressProtocolGuid, NULL, (VOID **) &Decompress);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+ Status = Decompress->GetInfo (
+ Decompress,
+ ImageBuffer,
+ ImageLength,
+ &DestinationSize,
+ &ScratchSize
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (Buffer == NULL || *BufferSize < DestinationSize) {
+ *BufferSize = DestinationSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *BufferSize = DestinationSize;
+ Scratch = AllocatePool (ScratchSize);
+ if (Scratch == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = Decompress->Decompress (
+ Decompress,
+ ImageBuffer,
+ ImageLength,
+ Buffer,
+ DestinationSize,
+ Scratch,
+ ScratchSize
+ );
+ FreePool (Scratch);
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Initialize a PCI LoadFile2 instance.
+
+ @param PciIoDevice PCI IO Device.
+
+**/
+VOID
+InitializePciLoadFile2 (
+ IN PCI_IO_DEVICE *PciIoDevice
+ )
+{
+ PciIoDevice->LoadFile2.LoadFile = LoadFile2;
+}
+
+/**
+ Causes the driver to load a specified file.
+
+ @param This Indicates a pointer to the calling context.
+ @param FilePath The device specific path of the file to load.
+ @param BootPolicy Should always be FALSE.
+ @param BufferSize On input the size of Buffer in bytes. On output with a return
+ code of EFI_SUCCESS, the amount of data transferred to Buffer.
+ On output with a return code of EFI_BUFFER_TOO_SMALL,
+ the size of Buffer required to retrieve the requested file.
+ @param Buffer The memory buffer to transfer the file to. If Buffer is NULL,
+ then no the size of the requested file is returned in BufferSize.
+
+ @retval EFI_SUCCESS The file was loaded.
+ @retval EFI_UNSUPPORTED BootPolicy is TRUE.
+ @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
+ BufferSize is NULL.
+ @retval EFI_NOT_FOUND Not found PCI Option Rom on PCI device.
+ @retval EFI_DEVICE_ERROR Failed to decompress PCI Option Rom image.
+ @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
+ BufferSize has been updated with the size needed to complete the request.
+
+**/
+EFI_STATUS
+EFIAPI
+LoadFile2 (
+ IN EFI_LOAD_FILE2_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN BOOLEAN BootPolicy,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer OPTIONAL
+ )
+{
+ PCI_IO_DEVICE *PciIoDevice;
+
+ if (BootPolicy) {
+ return EFI_UNSUPPORTED;
+ }
+ PciIoDevice = PCI_IO_DEVICE_FROM_LOAD_FILE2_THIS (This);
+
+ return LocalLoadFile2 (
+ PciIoDevice,
+ FilePath,
+ BufferSize,
+ Buffer
+ );
+}
+
+/**
+ Get Pci device's oprom information.
+
+ @param PciIoDevice Input Pci device instance.
+ Output Pci device instance with updated OptionRom size.
+
+ @retval EFI_NOT_FOUND Pci device has not Option Rom.
+ @retval EFI_SUCCESS Pci device has Option Rom.
+
+**/
+EFI_STATUS
+GetOpRomInfo (
+ IN OUT PCI_IO_DEVICE *PciIoDevice
+ )
+{
+ UINT8 RomBarIndex;
+ UINT32 AllOnes;
+ UINT64 Address;
+ EFI_STATUS Status;
+ UINT8 Bus;
+ UINT8 Device;
+ UINT8 Function;
+ EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
+
+ Bus = PciIoDevice->BusNumber;
+ Device = PciIoDevice->DeviceNumber;
+ Function = PciIoDevice->FunctionNumber;
+
+ PciRootBridgeIo = PciIoDevice->PciRootBridgeIo;
+
+ //
+ // Offset is 0x30 if is not ppb
+ //
+
+ //
+ // 0x30
+ //
+ RomBarIndex = PCI_EXPANSION_ROM_BASE;
+
+ if (IS_PCI_BRIDGE (&PciIoDevice->Pci)) {
+ //
+ // If is ppb, 0x38
+ //
+ RomBarIndex = PCI_BRIDGE_ROMBAR;
+ }
+ //
+ // The bit0 is 0 to prevent the enabling of the Rom address decoder
+ //
+ AllOnes = 0xfffffffe;
+ Address = EFI_PCI_ADDRESS (Bus, Device, Function, RomBarIndex);
+
+ Status = PciRootBridgeIo->Pci.Write (
+ PciRootBridgeIo,
+ EfiPciWidthUint32,
+ Address,
+ 1,
+ &AllOnes
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Read back
+ //
+ Status = PciRootBridgeIo->Pci.Read(
+ PciRootBridgeIo,
+ EfiPciWidthUint32,
+ Address,
+ 1,
+ &AllOnes
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Bits [1, 10] are reserved
+ //
+ AllOnes &= 0xFFFFF800;
+ if ((AllOnes == 0) || (AllOnes == 0xFFFFF800)) {
+ return EFI_NOT_FOUND;
+ }
+
+ PciIoDevice->RomSize = (~AllOnes) + 1;
+ return EFI_SUCCESS;
+}
+
+/**
+ Check if the RomImage contains EFI Images.
+
+ @param RomImage The ROM address of Image for check.
+ @param RomSize Size of ROM for check.
+
+ @retval TRUE ROM contain EFI Image.
+ @retval FALSE ROM not contain EFI Image.
+
+**/
+BOOLEAN
+ContainEfiImage (
+ IN VOID *RomImage,
+ IN UINT64 RomSize
+ )
+{
+ PCI_EXPANSION_ROM_HEADER *RomHeader;
+ PCI_DATA_STRUCTURE *RomPcir;
+ UINT8 Indicator;
+
+ Indicator = 0;
+ RomHeader = RomImage;
+ if (RomHeader == NULL) {
+ return FALSE;
+ }
+
+ do {
+ if (RomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
+ RomHeader = (PCI_EXPANSION_ROM_HEADER *) ((UINT8 *) RomHeader + 512);
+ continue;
+ }
+
+ //
+ // The PCI Data Structure must be DWORD aligned.
+ //
+ if (RomHeader->PcirOffset == 0 ||
+ (RomHeader->PcirOffset & 3) != 0 ||
+ (UINT8 *) RomHeader + RomHeader->PcirOffset + sizeof (PCI_DATA_STRUCTURE) > (UINT8 *) RomImage + RomSize) {
+ break;
+ }
+
+ RomPcir = (PCI_DATA_STRUCTURE *) ((UINT8 *) RomHeader + RomHeader->PcirOffset);
+ if (RomPcir->Signature != PCI_DATA_STRUCTURE_SIGNATURE) {
+ break;
+ }
+
+ if (RomPcir->CodeType == PCI_CODE_TYPE_EFI_IMAGE) {
+ return TRUE;
+ }
+
+ Indicator = RomPcir->Indicator;
+ RomHeader = (PCI_EXPANSION_ROM_HEADER *) ((UINT8 *) RomHeader + RomPcir->ImageLength * 512);
+ } while (((UINT8 *) RomHeader < (UINT8 *) RomImage + RomSize) && ((Indicator & 0x80) == 0x00));
+
+ return FALSE;
+}
+
+/**
+ Load Option Rom image for specified PCI device.
+
+ @param PciDevice Pci device instance.
+ @param RomBase Base address of Option Rom.
+
+ @retval EFI_OUT_OF_RESOURCES No enough memory to hold image.
+ @retval EFI_SUCESS Successfully loaded Option Rom.
+
+**/
+EFI_STATUS
+LoadOpRomImage (
+ IN PCI_IO_DEVICE *PciDevice,
+ IN UINT64 RomBase
+ )
+{
+ UINT8 RomBarIndex;
+ UINT8 Indicator;
+ UINT16 OffsetPcir;
+ UINT32 RomBarOffset;
+ UINT32 RomBar;
+ EFI_STATUS RetStatus;
+ BOOLEAN FirstCheck;
+ UINT8 *Image;
+ PCI_EXPANSION_ROM_HEADER *RomHeader;
+ PCI_DATA_STRUCTURE *RomPcir;
+ UINT64 RomSize;
+ UINT64 RomImageSize;
+ UINT32 LegacyImageLength;
+ UINT8 *RomInMemory;
+ UINT8 CodeType;
+
+ RomSize = PciDevice->RomSize;
+
+ Indicator = 0;
+ RomImageSize = 0;
+ RomInMemory = NULL;
+ CodeType = 0xFF;
+
+ //
+ // Get the RomBarIndex
+ //
+
+ //
+ // 0x30
+ //
+ RomBarIndex = PCI_EXPANSION_ROM_BASE;
+ if (IS_PCI_BRIDGE (&(PciDevice->Pci))) {
+ //
+ // if is ppb
+ //
+
+ //
+ // 0x38
+ //
+ RomBarIndex = PCI_BRIDGE_ROMBAR;
+ }
+ //
+ // Allocate memory for Rom header and PCIR
+ //
+ RomHeader = AllocatePool (sizeof (PCI_EXPANSION_ROM_HEADER));
+ if (RomHeader == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ RomPcir = AllocatePool (sizeof (PCI_DATA_STRUCTURE));
+ if (RomPcir == NULL) {
+ FreePool (RomHeader);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ RomBar = (UINT32) RomBase;
+
+ //
+ // Enable RomBar
+ //
+ RomDecode (PciDevice, RomBarIndex, RomBar, TRUE);
+
+ RomBarOffset = RomBar;
+ RetStatus = EFI_NOT_FOUND;
+ FirstCheck = TRUE;
+ LegacyImageLength = 0;
+
+ do {
+ PciDevice->PciRootBridgeIo->Mem.Read (
+ PciDevice->PciRootBridgeIo,
+ EfiPciWidthUint8,
+ RomBarOffset,
+ sizeof (PCI_EXPANSION_ROM_HEADER),
+ (UINT8 *) RomHeader
+ );
+
+ if (RomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
+ RomBarOffset = RomBarOffset + 512;
+ if (FirstCheck) {
+ break;
+ } else {
+ RomImageSize = RomImageSize + 512;
+ continue;
+ }
+ }
+
+ FirstCheck = FALSE;
+ OffsetPcir = RomHeader->PcirOffset;
+ //
+ // If the pointer to the PCI Data Structure is invalid, no further images can be located.
+ // The PCI Data Structure must be DWORD aligned.
+ //
+ if (OffsetPcir == 0 ||
+ (OffsetPcir & 3) != 0 ||
+ RomImageSize + OffsetPcir + sizeof (PCI_DATA_STRUCTURE) > RomSize) {
+ break;
+ }
+ PciDevice->PciRootBridgeIo->Mem.Read (
+ PciDevice->PciRootBridgeIo,
+ EfiPciWidthUint8,
+ RomBarOffset + OffsetPcir,
+ sizeof (PCI_DATA_STRUCTURE),
+ (UINT8 *) RomPcir
+ );
+ //
+ // If a valid signature is not present in the PCI Data Structure, no further images can be located.
+ //
+ if (RomPcir->Signature != PCI_DATA_STRUCTURE_SIGNATURE) {
+ break;
+ }
+ if (RomImageSize + RomPcir->ImageLength * 512 > RomSize) {
+ break;
+ }
+ if (RomPcir->CodeType == PCI_CODE_TYPE_PCAT_IMAGE) {
+ CodeType = PCI_CODE_TYPE_PCAT_IMAGE;
+ LegacyImageLength = ((UINT32)((EFI_LEGACY_EXPANSION_ROM_HEADER *)RomHeader)->Size512) * 512;
+ }
+ Indicator = RomPcir->Indicator;
+ RomImageSize = RomImageSize + RomPcir->ImageLength * 512;
+ RomBarOffset = RomBarOffset + RomPcir->ImageLength * 512;
+ } while (((Indicator & 0x80) == 0x00) && ((RomBarOffset - RomBar) < RomSize));
+
+ //
+ // Some Legacy Cards do not report the correct ImageLength so used the maximum
+ // of the legacy length and the PCIR Image Length
+ //
+ if (CodeType == PCI_CODE_TYPE_PCAT_IMAGE) {
+ RomImageSize = MAX (RomImageSize, LegacyImageLength);
+ }
+
+ if (RomImageSize > 0) {
+ RetStatus = EFI_SUCCESS;
+ Image = AllocatePool ((UINT32) RomImageSize);
+ if (Image == NULL) {
+ RomDecode (PciDevice, RomBarIndex, RomBar, FALSE);
+ FreePool (RomHeader);
+ FreePool (RomPcir);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Copy Rom image into memory
+ //
+ PciDevice->PciRootBridgeIo->Mem.Read (
+ PciDevice->PciRootBridgeIo,
+ EfiPciWidthUint8,
+ RomBar,
+ (UINT32) RomImageSize,
+ Image
+ );
+ RomInMemory = Image;
+ }
+
+ RomDecode (PciDevice, RomBarIndex, RomBar, FALSE);
+
+ PciDevice->EmbeddedRom = TRUE;
+ PciDevice->PciIo.RomSize = RomImageSize;
+ PciDevice->PciIo.RomImage = RomInMemory;
+
+ //
+ // For OpROM read from PCI device:
+ // Add the Rom Image to internal database for later PCI light enumeration
+ //
+ PciRomAddImageMapping (
+ NULL,
+ PciDevice->PciRootBridgeIo->SegmentNumber,
+ PciDevice->BusNumber,
+ PciDevice->DeviceNumber,
+ PciDevice->FunctionNumber,
+ PciDevice->PciIo.RomImage,
+ PciDevice->PciIo.RomSize
+ );
+
+ //
+ // Free allocated memory
+ //
+ FreePool (RomHeader);
+ FreePool (RomPcir);
+
+ return RetStatus;
+}
+
+/**
+ Enable/Disable Option Rom decode.
+
+ @param PciDevice Pci device instance.
+ @param RomBarIndex The BAR index of the standard PCI Configuration header to use as the
+ base address for resource range. The legal range for this field is 0..5.
+ @param RomBar Base address of Option Rom.
+ @param Enable Flag for enable/disable decode.
+
+**/
+VOID
+RomDecode (
+ IN PCI_IO_DEVICE *PciDevice,
+ IN UINT8 RomBarIndex,
+ IN UINT32 RomBar,
+ IN BOOLEAN Enable
+ )
+{
+ UINT32 Value32;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+
+ PciIo = &PciDevice->PciIo;
+ if (Enable) {
+
+ //
+ // set the Rom base address: now is hardcode
+ // enable its decoder
+ //
+ Value32 = RomBar | 0x1;
+ PciIo->Pci.Write (
+ PciIo,
+ (EFI_PCI_IO_PROTOCOL_WIDTH) EfiPciWidthUint32,
+ RomBarIndex,
+ 1,
+ &Value32
+ );
+
+ //
+ // Programe all upstream bridge
+ //
+ ProgramUpstreamBridgeForRom (PciDevice, RomBar, TRUE);
+
+ //
+ // Setting the memory space bit in the function's command register
+ //
+ PCI_ENABLE_COMMAND_REGISTER(PciDevice, EFI_PCI_COMMAND_MEMORY_SPACE);
+
+ } else {
+
+ //
+ // disable command register decode to memory
+ //
+ PCI_DISABLE_COMMAND_REGISTER(PciDevice, EFI_PCI_COMMAND_MEMORY_SPACE);
+
+ //
+ // Destroy the programmed bar in all the upstream bridge.
+ //
+ ProgramUpstreamBridgeForRom (PciDevice, RomBar, FALSE);
+
+ //
+ // disable rom decode
+ //
+ Value32 = 0xFFFFFFFE;
+ PciIo->Pci.Write (
+ PciIo,
+ (EFI_PCI_IO_PROTOCOL_WIDTH) EfiPciWidthUint32,
+ RomBarIndex,
+ 1,
+ &Value32
+ );
+
+ }
+}
+
+/**
+ Load and start the Option Rom image.
+
+ @param PciDevice Pci device instance.
+
+ @retval EFI_SUCCESS Successfully loaded and started PCI Option Rom image.
+ @retval EFI_NOT_FOUND Failed to process PCI Option Rom image.
+
+**/
+EFI_STATUS
+ProcessOpRomImage (
+ IN PCI_IO_DEVICE *PciDevice
+ )
+{
+ UINT8 Indicator;
+ UINT32 ImageSize;
+ VOID *RomBar;
+ UINT8 *RomBarOffset;
+ EFI_HANDLE ImageHandle;
+ EFI_STATUS Status;
+ EFI_STATUS RetStatus;
+ EFI_PCI_EXPANSION_ROM_HEADER *EfiRomHeader;
+ PCI_DATA_STRUCTURE *Pcir;
+ EFI_DEVICE_PATH_PROTOCOL *PciOptionRomImageDevicePath;
+ MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH EfiOpRomImageNode;
+ VOID *Buffer;
+ UINTN BufferSize;
+
+ Indicator = 0;
+
+ //
+ // Get the Address of the Option Rom image
+ //
+ RomBar = PciDevice->PciIo.RomImage;
+ RomBarOffset = (UINT8 *) RomBar;
+ RetStatus = EFI_NOT_FOUND;
+
+ if (RomBar == NULL) {
+ return RetStatus;
+ }
+ ASSERT (((EFI_PCI_EXPANSION_ROM_HEADER *) RomBarOffset)->Signature == PCI_EXPANSION_ROM_HEADER_SIGNATURE);
+
+ do {
+ EfiRomHeader = (EFI_PCI_EXPANSION_ROM_HEADER *) RomBarOffset;
+ if (EfiRomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
+ RomBarOffset += 512;
+ continue;
+ }
+
+ Pcir = (PCI_DATA_STRUCTURE *) (RomBarOffset + EfiRomHeader->PcirOffset);
+ ASSERT (Pcir->Signature == PCI_DATA_STRUCTURE_SIGNATURE);
+ ImageSize = (UINT32) (Pcir->ImageLength * 512);
+ Indicator = Pcir->Indicator;
+
+ //
+ // Skip the image if it is not an EFI PCI Option ROM image
+ //
+ if (Pcir->CodeType != PCI_CODE_TYPE_EFI_IMAGE) {
+ goto NextImage;
+ }
+
+ //
+ // Ignore the EFI PCI Option ROM image if it is an EFI application
+ //
+ if (EfiRomHeader->EfiSubsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) {
+ goto NextImage;
+ }
+
+ //
+ // Create Pci Option Rom Image device path header
+ //
+ EfiOpRomImageNode.Header.Type = MEDIA_DEVICE_PATH;
+ EfiOpRomImageNode.Header.SubType = MEDIA_RELATIVE_OFFSET_RANGE_DP;
+ SetDevicePathNodeLength (&EfiOpRomImageNode.Header, sizeof (EfiOpRomImageNode));
+ EfiOpRomImageNode.StartingOffset = (UINTN) RomBarOffset - (UINTN) RomBar;
+ EfiOpRomImageNode.EndingOffset = (UINTN) RomBarOffset + ImageSize - 1 - (UINTN) RomBar;
+
+ PciOptionRomImageDevicePath = AppendDevicePathNode (PciDevice->DevicePath, &EfiOpRomImageNode.Header);
+ ASSERT (PciOptionRomImageDevicePath != NULL);
+
+ //
+ // load image and start image
+ //
+ BufferSize = 0;
+ Buffer = NULL;
+ ImageHandle = NULL;
+
+ Status = gBS->LoadImage (
+ FALSE,
+ gPciBusDriverBinding.DriverBindingHandle,
+ PciOptionRomImageDevicePath,
+ Buffer,
+ BufferSize,
+ &ImageHandle
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // Record the Option ROM Image device path when LoadImage fails.
+ // PciOverride.GetDriver() will try to look for the Image Handle using the device path later.
+ //
+ AddDriver (PciDevice, NULL, PciOptionRomImageDevicePath);
+ } else {
+ Status = gBS->StartImage (ImageHandle, NULL, NULL);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Record the Option ROM Image Handle
+ //
+ AddDriver (PciDevice, ImageHandle, NULL);
+ PciRomAddImageMapping (
+ ImageHandle,
+ PciDevice->PciRootBridgeIo->SegmentNumber,
+ PciDevice->BusNumber,
+ PciDevice->DeviceNumber,
+ PciDevice->FunctionNumber,
+ PciDevice->PciIo.RomImage,
+ PciDevice->PciIo.RomSize
+ );
+ RetStatus = EFI_SUCCESS;
+ }
+ }
+ FreePool (PciOptionRomImageDevicePath);
+
+NextImage:
+ RomBarOffset += ImageSize;
+
+ } while (((Indicator & 0x80) == 0x00) && (((UINTN) RomBarOffset - (UINTN) RomBar) < PciDevice->RomSize));
+
+ return RetStatus;
+}
+