/*++
Copyright (c) 2005 - 2012, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
Module Name:
PciOptionRomSupport.c
Abstract:
PCI Bus Driver
Revision History
--*/
#include "PciBus.h"
EFI_STATUS
RomDecode (
IN PCI_IO_DEVICE *PciDevice,
IN UINT8 RomBarIndex,
IN UINT32 RomBar,
IN BOOLEAN Enable
);
EFI_STATUS
GetOpRomInfo (
IN PCI_IO_DEVICE *PciIoDevice
)
/*++
Routine Description:
Arguments:
Returns:
--*/
{
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 Status;
}
//
// read back
//
Status = PciRootBridgeIo->Pci.Read (
PciRootBridgeIo,
EfiPciWidthUint32,
Address,
1,
&AllOnes
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Bits [1, 10] are reserved
//
AllOnes &= 0xFFFFF800;
if ((AllOnes == 0) || (AllOnes == 0xFFFFF800)) {
return EFI_NOT_FOUND;
}
DEBUG ((EFI_D_ERROR, "PCIBUS: GetOpRomInfo: OPROM detected!\n"));
DEBUG ((EFI_D_ERROR, "PCIBUS: GetOpRomInfo: B-%x, D-%x, F-%x\n", (UINTN)Bus, (UINTN)Device, (UINTN)Function));
PciIoDevice->RomSize = (UINT64) ((~AllOnes) + 1);
return EFI_SUCCESS;
}
EFI_STATUS
LoadOpRomImage (
IN PCI_IO_DEVICE *PciDevice,
IN UINT64 ReservedMemoryBase
)
/*++
Routine Description:
Load option rom image for specified PCI device
Arguments:
Returns:
--*/
{
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) {
gBS->FreePool (RomHeader);
return EFI_OUT_OF_RESOURCES;
}
RomBar = (UINT32)ReservedMemoryBase;
//
// 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);
gBS->FreePool (RomHeader);
gBS->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->PciIo.RomSize = RomImageSize;
PciDevice->PciIo.RomImage = RomInMemory;
//
// Free allocated memory
//
gBS->FreePool (RomHeader);
gBS->FreePool (RomPcir);
return retStatus;
}
EFI_STATUS
RomDecode (
IN PCI_IO_DEVICE *PciDevice,
IN UINT8 RomBarIndex,
IN UINT32 RomBar,
IN BOOLEAN Enable
)
/*++
Routine Description:
Arguments:
Returns:
--*/
{
UINT16 CommandValue;
UINT32 Value32;
UINT64 Address;
//EFI_STATUS Status;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
PciRootBridgeIo = PciDevice->PciRootBridgeIo;
if (Enable) {
Address = EFI_PCI_ADDRESS (PciDevice->BusNumber, PciDevice->DeviceNumber, PciDevice->FunctionNumber, RomBarIndex);
//
// set the Rom base address: now is hardcode
//
PciRootBridgeIo->Pci.Write(
PciRootBridgeIo,
EfiPciWidthUint32,
Address,
1,
&RomBar);
//
// enable its decoder
//
Value32 = RomBar | 0x1;
PciRootBridgeIo->Pci.Write(
PciRootBridgeIo,
EfiPciWidthUint32,
Address,
1,
&Value32);
//
//setting the memory space bit in the function's command register
//
Address = EFI_PCI_ADDRESS (PciDevice->BusNumber, PciDevice->DeviceNumber, PciDevice->FunctionNumber, 0x04);
PciRootBridgeIo->Pci.Read(
PciRootBridgeIo,
EfiPciWidthUint16,
Address,
1,
&CommandValue);
CommandValue = (UINT16)(CommandValue | 0x0002); //0x0003
PciRootBridgeIo->Pci.Write(
PciRootBridgeIo,
EfiPciWidthUint16,
Address,
1,
&CommandValue);
} else {
//
// disable rom decode
//
Address = EFI_PCI_ADDRESS (PciDevice->BusNumber, PciDevice->DeviceNumber, PciDevice->FunctionNumber, RomBarIndex);
Value32 = 0xfffffffe;
PciRootBridgeIo->Pci.Write(
PciRootBridgeIo,
EfiPciWidthUint32,
Address,
1,
&Value32);
}
return EFI_SUCCESS;
}
EFI_STATUS
ProcessOpRomImage (
PCI_IO_DEVICE *PciDevice
)
/*++
Routine Description:
Process the oprom image.
Arguments:
PciDevice A pointer to a pci device.
Returns:
EFI Status.
--*/
{
UINT8 Indicator;
UINT32 ImageSize;
UINT16 ImageOffset;
VOID *RomBar;
UINT8 *RomBarOffset;
EFI_HANDLE ImageHandle;
EFI_STATUS Status;
EFI_STATUS retStatus;
BOOLEAN SkipImage;
UINT32 DestinationSize;
UINT32 ScratchSize;
UINT8 *Scratch;
VOID *ImageBuffer;
VOID *DecompressedImageBuffer;
UINT32 ImageLength;
EFI_DECOMPRESS_PROTOCOL *Decompress;
EFI_PCI_EXPANSION_ROM_HEADER *EfiRomHeader;
PCI_DATA_STRUCTURE *Pcir;
UINT32 InitializationSize;
Indicator = 0;
//
// Get the Address of the Rom image
//
RomBar = PciDevice->PciIo.RomImage;
RomBarOffset = (UINT8 *) RomBar;
retStatus = EFI_NOT_FOUND;
if (RomBarOffset == 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 = 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;
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))) {
ImageOffset = EfiRomHeader->EfiImageHeaderOffset;
InitializationSize = EfiRomHeader->InitializationSize * 512;
if (InitializationSize <= ImageSize && ImageOffset < InitializationSize) {
ImageBuffer = (VOID *) (RomBarOffset + ImageOffset);
ImageLength = InitializationSize - (UINT32)ImageOffset;
DecompressedImageBuffer = NULL;
//
// decompress here if needed
//
SkipImage = FALSE;
if (EfiRomHeader->CompressionType > EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED) {
SkipImage = TRUE;
}
if (EfiRomHeader->CompressionType == EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED) {
Status = gBS->LocateProtocol (&gEfiDecompressProtocolGuid, NULL, (VOID **) &Decompress);
if (EFI_ERROR (Status)) {
SkipImage = TRUE;
} else {
SkipImage = TRUE;
Status = Decompress->GetInfo (
Decompress,
ImageBuffer,
ImageLength,
&DestinationSize,
&ScratchSize
);
if (!EFI_ERROR (Status)) {
DecompressedImageBuffer = NULL;
DecompressedImageBuffer = AllocatePool (DestinationSize);
if (DecompressedImageBuffer != NULL) {
Scratch = AllocatePool (ScratchSize);
if (Scratch != NULL) {
Status = Decompress->Decompress (
Decompress,
ImageBuffer,
ImageLength,
DecompressedImageBuffer,
DestinationSize,
Scratch,
ScratchSize
);
if (!EFI_ERROR (Status)) {
ImageBuffer = DecompressedImageBuffer;
ImageLength = DestinationSize;
SkipImage = FALSE;
}
gBS->FreePool (Scratch);
}
}
}
}
}
if (!SkipImage) {
//
// load image and start image
//
Status = gBS->LoadImage (
FALSE,
gPciBusDriverBinding.DriverBindingHandle,
NULL,
ImageBuffer,
ImageLength,
&ImageHandle
);
if (!EFI_ERROR (Status)) {
Status = gBS->StartImage (ImageHandle, NULL, NULL);
if (!EFI_ERROR (Status)) {
AddDriver (PciDevice, ImageHandle);
retStatus = EFI_SUCCESS;
}
}
}
RomBarOffset = RomBarOffset + ImageSize;
} else {
RomBarOffset = RomBarOffset + ImageSize;
}
} else {
RomBarOffset = RomBarOffset + ImageSize;
}
} while (((Indicator & 0x80) == 0x00) && ((UINTN) (RomBarOffset - (UINT8 *) RomBar) < PciDevice->RomSize));
return retStatus;
}