mirror of
				https://github.com/acidanthera/audk.git
				synced 2025-10-31 11:13:53 +01:00 
			
		
		
		
	A faulty PCI device has the Option ROM image size set to 0. UEFI reads two headers PCI_EXPANSION_ROM_HEADER and PCI_DATA_STRUCTURE to get the Option ROM information. Because the image size is 0, the Option ROM header address never changes. As a result, UEFI keeps reading the same two headers definitely. This patch is intended to fix it. Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Ray Ni <ray.ni@intel.com> Signed-off-by: Nhi Pham <nhi@os.amperecomputing.com> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
		
			
				
	
	
		
			771 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			771 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @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) && (RomImageSize > 0));
 | |
| 
 | |
|   //
 | |
|   // Some Legacy Cards do not report the correct ImageLength so used the maximum
 | |
|   // of the legacy length and the PCIR Image Length
 | |
|   //
 | |
|   if ((RomImageSize > 0) && (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,
 | |
|                                       EfiPciWidthUint32,
 | |
|                                       RomBar,
 | |
|                                       (UINT32)RomImageSize/sizeof (UINT32),
 | |
|                                       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;
 | |
| }
 |