| /*++ | |
| 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; | |
| } |