/**@file Copyright (c) 2006 - 2007, Intel Corporation All rights reserved. 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. **/ #include "PciBus.h" #include "PciResourceSupport.h" #include // // Module global for a template of the PCI option ROM Image Device Path Node // MEMMAP_DEVICE_PATH mPciOptionRomImageDevicePathNodeTemplate = { { HARDWARE_DEVICE_PATH, HW_MEMMAP_DP, sizeof (MEMMAP_DEVICE_PATH) }, EfiMemoryMappedIO, 0, 0 }; /** Get Pci device's oprom infor bits. @retval EFI_NOT_FOUND Pci device has not oprom @retval EFI_SUCCESS Pci device has oprom **/ EFI_STATUS GetOpRomInfo ( IN 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_DEVICE_ROMBAR; 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 = PciRootBridgeIoWrite ( PciRootBridgeIo, &PciIoDevice->Pci, EfiPciWidthUint32, Address, 1, &AllOnes ); if (EFI_ERROR (Status)) { return Status; } // // read back // Status = PciRootBridgeIoRead ( PciRootBridgeIo, &PciIoDevice->Pci, 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; } PciIoDevice->RomSize = (UINT64) ((~AllOnes) + 1); return EFI_SUCCESS; } /** Load option rom image for specified PCI device @param PciDevice Pci device instance @param RomBase Base address of oprom. @retval EFI_OUT_OF_RESOURCES not enough memory to hold image @retval EFI_SUCESS Success **/ 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; UINT8 *RomInMemory; UINT8 CodeType; RomSize = PciDevice->RomSize; Indicator = 0; RomImageSize = 0; RomInMemory = NULL; CodeType = 0xFF; // // Get the RomBarIndex // // // 0x30 // RomBarIndex = PCI_DEVICE_ROMBAR; 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) RomBase; // // Enable RomBar // RomDecode (PciDevice, RomBarIndex, RomBar, TRUE); RomBarOffset = RomBar; retStatus = EFI_NOT_FOUND; FirstCheck = TRUE; 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; PciDevice->PciRootBridgeIo->Mem.Read ( PciDevice->PciRootBridgeIo, EfiPciWidthUint8, RomBarOffset + OffsetPcir, sizeof (PCI_DATA_STRUCTURE), (UINT8 *) RomPcir ); if (RomPcir->CodeType == PCI_CODE_TYPE_PCAT_IMAGE) { CodeType = PCI_CODE_TYPE_PCAT_IMAGE; } 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, (((EFI_LEGACY_EXPANSION_ROM_HEADER *)RomHeader)->Size512 * 512)); } 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; // // 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, (UINT64) (UINTN) PciDevice->PciIo.RomImage, PciDevice->PciIo.RomSize ); // // Free allocated memory // gBS->FreePool (RomHeader); gBS->FreePool (RomPcir); return retStatus; } /** enable/disable oprom 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 rom @param Enable Flag for enable/disable decode. @retval EFI_SUCCESS Success **/ EFI_STATUS RomDecode ( IN PCI_IO_DEVICE *PciDevice, IN UINT8 RomBarIndex, IN UINT32 RomBar, IN BOOLEAN Enable ) { UINT32 Value32; UINT32 Offset; EFI_PCI_IO_PROTOCOL *PciIo; PciIo = &PciDevice->PciIo; if (Enable) { // // Clear all bars // for (Offset = 0x10; Offset <= 0x24; Offset += sizeof (UINT32)) { PciIoWrite (PciIo, EfiPciIoWidthUint32, Offset, 1, &gAllZero); } // // set the Rom base address: now is hardcode // enable its decoder // Value32 = RomBar | 0x1; PciIoWrite ( PciIo, (EFI_PCI_IO_PROTOCOL_WIDTH) EfiPciWidthUint32, RomBarIndex, 1, &Value32 ); // // Programe all upstream bridge // ProgrameUpstreamBridgeForRom(PciDevice, RomBar, TRUE); // // Setting the memory space bit in the function's command register // PciEnableCommandRegister(PciDevice, EFI_PCI_COMMAND_MEMORY_SPACE); } else { // // disable command register decode to memory // PciDisableCommandRegister(PciDevice, EFI_PCI_COMMAND_MEMORY_SPACE); // // Destroy the programmed bar in all the upstream bridge. // ProgrameUpstreamBridgeForRom(PciDevice, RomBar, FALSE); // // disable rom decode // Value32 = 0xFFFFFFFE; PciIoWrite ( PciIo, (EFI_PCI_IO_PROTOCOL_WIDTH) EfiPciWidthUint32, RomBarIndex, 1, &Value32 ); } return EFI_SUCCESS; } /** Process the oprom image. @param PciDevice Pci device instance **/ EFI_STATUS ProcessOpRomImage ( PCI_IO_DEVICE *PciDevice ) { UINT8 Indicator; UINT32 ImageSize; UINT16 ImageOffset; VOID *RomBar; UINT8 *RomBarOffset; EFI_HANDLE ImageHandle; EFI_STATUS Status; EFI_STATUS retStatus; BOOLEAN FirstCheck; 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; EFI_DEVICE_PATH_PROTOCOL *PciOptionRomImageDevicePath; Indicator = 0; // // Get the Address of the Rom image // RomBar = PciDevice->PciIo.RomImage; RomBarOffset = (UINT8 *) RomBar; retStatus = EFI_NOT_FOUND; FirstCheck = TRUE; do { EfiRomHeader = (EFI_PCI_EXPANSION_ROM_HEADER *) RomBarOffset; if (EfiRomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) { RomBarOffset = RomBarOffset + 512; if (FirstCheck) { break; } else { continue; } } FirstCheck = FALSE; Pcir = (PCI_DATA_STRUCTURE *) (RomBarOffset + EfiRomHeader->PcirOffset); ImageSize = (UINT32) (Pcir->ImageLength * 512); Indicator = Pcir->Indicator; if ((Pcir->CodeType == PCI_CODE_TYPE_EFI_IMAGE) && (EfiRomHeader->EfiSignature == EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE)) { if ((EfiRomHeader->EfiSubsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) || (EfiRomHeader->EfiSubsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER)) { ImageOffset = EfiRomHeader->EfiImageHeaderOffset; ImageSize = (UINT32) (EfiRomHeader->InitializationSize * 512); ImageBuffer = (VOID *) (RomBarOffset + ImageOffset); ImageLength = ImageSize - (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) { // // Build Memory Mapped device path node to record the image offset into the PCI Option ROM // mPciOptionRomImageDevicePathNodeTemplate.StartingAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) (RomBarOffset - (UINT8 *) RomBar); mPciOptionRomImageDevicePathNodeTemplate.EndingAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) (RomBarOffset + ImageSize - 1 - (UINT8 *) RomBar); PciOptionRomImageDevicePath = AppendDevicePathNode (PciDevice->DevicePath, (const EFI_DEVICE_PATH_PROTOCOL *)&mPciOptionRomImageDevicePathNodeTemplate); ASSERT (PciOptionRomImageDevicePath != NULL); // // load image and start image // Status = gBS->LoadImage ( FALSE, gPciBusDriverBinding.DriverBindingHandle, PciOptionRomImageDevicePath, ImageBuffer, ImageLength, &ImageHandle ); // // Free the device path after it has been used by LoadImage // gBS->FreePool (PciOptionRomImageDevicePath); if (!EFI_ERROR (Status)) { Status = gBS->StartImage (ImageHandle, NULL, NULL); if (!EFI_ERROR (Status)) { AddDriver (PciDevice, ImageHandle); PciRomAddImageMapping ( ImageHandle, PciDevice->PciRootBridgeIo->SegmentNumber, PciDevice->BusNumber, PciDevice->DeviceNumber, PciDevice->FunctionNumber, (UINT64) (UINTN) PciDevice->PciIo.RomImage, PciDevice->PciIo.RomSize ); 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; }