UefiPayloadPkg: Scan for Option ROMs

Install the gPciPlatformProtocol to scan for Option ROMs.

For every device we probe the Option ROM and provide a pointer
to the activated BAR if found.

It's safe to assume that all ROM bars have been enumerated,
reserved in the bridge resources and are disabled by default.
This is made a mandatory bootloader requirement in the next commit.

Enabling them and leaving them enabled will do no harm.

This can easily be tested on qemu, where it will start finding Option ROMs
for VGA and network cards.

Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Signed-off-by: Marcello Sylvester Bauer <marcello.bauer@9elements.com>
Cc: Patrick Rudolph <patrick.rudolph@9elements.com>
Cc: Christian Walter <christian.walter@9elements.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Nate DeSimone <nathaniel.l.desimone@intel.com>
Cc: Star Zeng <star.zeng@intel.com>
This commit is contained in:
Patrick Rudolph
2020-05-15 16:50:01 +02:00
committed by Tim Crawford
parent 8c767bb014
commit b9564773f1
5 changed files with 504 additions and 0 deletions

View File

@@ -0,0 +1,436 @@
/** @file
This driver will probe for the Option Rom and provide a pointer to
the activated BAR if found.
Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "PciPlatformDxe.h"
#include <Bus/Pci/PciBusDxe/PciBus.h>
#include <Bus/Pci/PciBusDxe/PciOptionRomSupport.h>
//
// The driver should only start on one graphics controller.
// So a global flag is used to remember that the driver is already started.
//
EFI_HANDLE mDriverHandle = NULL;
/**
The notification from the PCI bus enumerator to the platform that it is
about to enter a certain phase during the enumeration process.
@param[in] This The pointer to the EFI_PCI_PLATFORM_PROTOCOL instance.
@param[in] HostBridge The handle of the host bridge controller.
@param[in] Phase The phase of the PCI bus enumeration.
@param[in] ExecPhase Defines the execution phase of the PCI chipset driver.
@retval EFI_SUCCESS The function completed successfully.
**/
EFI_STATUS
EFIAPI
PciPlatformNotify(
IN EFI_PCI_PLATFORM_PROTOCOL *This,
IN EFI_HANDLE HostBridge,
IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PHASE Phase,
IN EFI_PCI_EXECUTION_PHASE ExecPhase
)
{
return EFI_SUCCESS;
}
/**
The notification from the PCI bus enumerator to the platform for each PCI
controller at several predefined points during PCI controller initialization.
@param[in] This The pointer to the EFI_PCI_PLATFORM_PROTOCOL instance.
@param[in] HostBridge The associated PCI host bridge handle.
@param[in] RootBridge The associated PCI root bridge handle.
@param[in] PciAddress The address of the PCI device on the PCI bus.
@param[in] Phase The phase of the PCI controller enumeration.
@param[in] ExecPhase Defines the execution phase of the PCI chipset driver.
@retval EFI_SUCCESS The function completed successfully.
**/
EFI_STATUS
EFIAPI
PciPlatformPrepController(
IN EFI_PCI_PLATFORM_PROTOCOL *This,
IN EFI_HANDLE HostBridge,
IN EFI_HANDLE RootBridge,
IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS PciAddress,
IN EFI_PCI_CONTROLLER_RESOURCE_ALLOCATION_PHASE Phase,
IN EFI_PCI_EXECUTION_PHASE ExecPhase
)
{
return EFI_SUCCESS;
}
/**
Gets the PCI device's option ROM.
@param[in] This The pointer to the EFI_PCI_PLATFORM_PROTOCOL instance.
@param[in] PciHandle The handle of the PCI device.
@param[out] RomImage If the call succeeds, the pointer to the pointer to the option ROM image.
Otherwise, this field is undefined. The memory for RomImage is allocated
by EFI_PCI_PLATFORM_PROTOCOL.GetPciRom() using the EFI Boot Service AllocatePool().
It is the caller's responsibility to free the memory using the EFI Boot Service
FreePool(), when the caller is done with the option ROM.
@param[out] RomSize If the call succeeds, a pointer to the size of the option ROM size. Otherwise,
this field is undefined.
@retval EFI_SUCCESS The option ROM was available for this device and loaded into memory.
@retval EFI_NOT_FOUND No option ROM was available for this device.
@retval EFI_OUT_OF_RESOURCES No memory was available to load the option ROM.
@retval EFI_DEVICE_ERROR An error occurred in obtaining the option ROM.
**/
EFI_STATUS
EFIAPI
PciGetPciRom (
IN CONST EFI_PCI_PLATFORM_PROTOCOL *This,
IN EFI_HANDLE PciHandle,
OUT VOID **RomImage,
OUT UINTN *RomSize
)
{
EFI_STATUS Status;
IN EFI_PCI_IO_PROTOCOL *PciIo;
UINTN RomBarIndex;
UINT32 Buffer;
UINT32 AllOnes;
PCI_IO_DEVICE *PciIoDevice;
UINT8 Indicator;
UINT16 OffsetPcir;
UINT32 RomBarOffset;
UINT32 RomBar;
BOOLEAN FirstCheck;
PCI_EXPANSION_ROM_HEADER *RomHeader;
PCI_DATA_STRUCTURE *RomPcir;
UINT64 RomImageSize;
UINT32 LegacyImageLength;
UINT8 *RomInMemory;
UINT8 CodeType;
if (!RomImage || !RomSize) {
return EFI_INVALID_PARAMETER;
}
*RomImage = NULL;
*RomSize = 0;
Status = gBS->HandleProtocol (
PciHandle,
&gEfiPciIoProtocolGuid,
(VOID **) &PciIo
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
DEBUG_CODE (
{
UINTN PciSegment;
UINTN PciBus;
UINTN PciDevice;
UINTN PciFunction;
//
// Get the location of the PCI device
//
Status = PciIo->GetLocation (
PciIo,
&PciSegment,
&PciBus,
&PciDevice,
&PciFunction
);
if (!EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "%a: Searching Option ROM on PCI @ [%02x|%02x|%02x] ..",
__FUNCTION__,
PciBus,
PciDevice,
PciFunction));
}
}
);
PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (PciIo);
//
// Offset is 0x30 if is not ppb
//
RomBarIndex = PCI_EXPANSION_ROM_BASE;
if (IS_PCI_BRIDGE (&PciIoDevice->Pci)) {
//
// If is ppb, 0x38
//
RomBarIndex = PCI_BRIDGE_ROMBAR;
}
//
// Backup BAR
//
Status = PciIo->Pci.Read (
PciIo,
EfiPciWidthUint32,
RomBarIndex,
1,
&Buffer
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "error accessing PCI device\n"));
goto CloseAndReturn;
return Status;
}
//
// The bit0 is 0 to prevent the enabling of the Rom address decoder
//
AllOnes = 0xfffffffe;
Status = PciIo->Pci.Write (
PciIo,
EfiPciWidthUint32,
RomBarIndex,
1,
&AllOnes
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "error accessing PCI device\n"));
goto CloseAndReturn;
}
//
// Read back
//
Status = PciIo->Pci.Read(
PciIo,
EfiPciWidthUint32,
RomBarIndex,
1,
&AllOnes
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "error accessing PCI device\n"));
goto CloseAndReturn;
}
//
// Bits [1, 10] are reserved
//
AllOnes &= 0xFFFFF800;
if ((AllOnes == 0) || (AllOnes == 0xFFFFF800)) {
DEBUG ((DEBUG_INFO, "nothing found\n"));
Status = EFI_NOT_FOUND;
goto CloseAndReturn;
}
*RomSize = (~AllOnes) + 1;
DEBUG ((DEBUG_INFO, "found one of size %d\n", *RomSize));
//
// Restore BAR and enable it
//
Buffer |= 1;
Status = PciIo->Pci.Write (
PciIo,
EfiPciWidthUint32,
RomBarIndex,
1,
&Buffer
);
if (EFI_ERROR (Status)) {
goto CloseAndReturn;
}
//
// Allocate memory for Rom header and PCIR
//
RomHeader = AllocatePool (sizeof (PCI_EXPANSION_ROM_HEADER));
if (RomHeader == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto CloseAndReturn;
}
RomPcir = AllocatePool (sizeof (PCI_DATA_STRUCTURE));
if (RomPcir == NULL) {
FreePool (RomHeader);
Status = EFI_OUT_OF_RESOURCES;
goto CloseAndReturn;
}
// FIXME: Use gEfiPciRootBridgeIoProtocolGuid
RomBar = (UINT32) Buffer &~1;
RomBarOffset = RomBar;
FirstCheck = TRUE;
LegacyImageLength = 0;
RomImageSize = 0;
do {
// FIXME: Use gEfiPciRootBridgeIoProtocolGuid
CopyMem(RomHeader, (VOID *)(UINTN)RomBarOffset, sizeof (PCI_EXPANSION_ROM_HEADER));
DEBUG ((DEBUG_INFO, "%a: RomHeader->Signature 0x%x\n", __FUNCTION__, RomHeader->Signature));
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;
}
// FIXME: Use gEfiPciRootBridgeIoProtocolGuid
CopyMem(RomPcir, (VOID *)(UINTN)RomBarOffset + OffsetPcir, sizeof (PCI_DATA_STRUCTURE));
DEBUG ((DEBUG_INFO, "%a: RomPcir->Signature 0x%x\n", __FUNCTION__, RomPcir->Signature));
//
// 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) {
// FIXME: Use gEfiPciRootBridgeIoProtocolGuid
RomInMemory = (VOID *)(UINTN)RomBar;
}
//
// Free allocated memory
//
FreePool (RomHeader);
FreePool (RomPcir);
if (RomImageSize > 0) {
*RomImage = RomInMemory;
*RomSize = RomImageSize;
DEBUG ((DEBUG_INFO, "%a: Found Option ROM at %p, length 0x%x\n", __FUNCTION__,
RomInMemory, RomImageSize));
Status = EFI_SUCCESS;
} else {
Status = EFI_NOT_FOUND;
}
CloseAndReturn:
//
// Close the I/O Abstraction(s) used to perform the supported test
//
gBS->CloseProtocol (
PciHandle,
&gEfiPciIoProtocolGuid,
PciIo,
PciHandle
);
return Status;
}
/**
Retrieves the platform policy regarding enumeration.
The GetPlatformPolicy() function retrieves the platform policy regarding PCI
enumeration. The PCI bus driver and the PCI Host Bridge Resource Allocation Protocol
driver can call this member function to retrieve the policy.
@param[in] This The pointer to the EFI_PCI_PLATFORM_PROTOCOL instance.
@param[out] PciPolicy The platform policy with respect to VGA and ISA aliasing.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER PciPolicy is NULL.
**/
EFI_STATUS
EFIAPI
PciGetPlatformPolicy (
IN CONST EFI_PCI_PLATFORM_PROTOCOL *This,
OUT EFI_PCI_PLATFORM_POLICY *PciPolicy
)
{
if (PciPolicy == NULL)
return EFI_INVALID_PARAMETER;
*PciPolicy = 0;
return EFI_SUCCESS;
}
EFI_PCI_PLATFORM_PROTOCOL mPciPlatformProtocol = {
PciPlatformNotify,
PciPlatformPrepController,
PciGetPlatformPolicy,
PciGetPciRom,
};
/**
The Entry Point for Option ROM driver.
It installs DriverBinding.
@retval EFI_SUCCESS The entry point is executed successfully.
@retval other Some error occurs when executing this entry point.
**/
EFI_STATUS
EFIAPI
InstallPciPlatformProtocol (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = gBS->InstallProtocolInterface (
&mDriverHandle,
&gEfiPciPlatformProtocolGuid,
EFI_NATIVE_INTERFACE,
&mPciPlatformProtocol
);
return Status;
}

View File

@@ -0,0 +1,19 @@
/** @file
Header file for a PCI platform driver.
Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef _PCI_PLATFORM_DXE_H_
#define _PCI_PLATFORM_DXE_H_
#include <PiDxe.h>
#include <IndustryStandard/Pci.h>
#include <IndustryStandard/Acpi.h>
#include <IndustryStandard/Pci22.h>
#include <Protocol/PciIo.h>
#include <Protocol/PciPlatform.h>
#endif

View File

@@ -0,0 +1,46 @@
## @file
# This driver produces gEfiPciPlatform protocol to load PCI Option ROMs
#
# Copyright (c) 2020, 9elements Agency GmbH
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = PciPlatformDxe
FILE_GUID = 86D58F7B-6E7C-401F-BDD4-E32E6D582AAD
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = InstallPciPlatformProtocol
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64
#
[Sources.common]
PciPlatformDxe.h
PciPlatformDxe.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
UefiDriverEntryPoint
UefiBootServicesTableLib
DxeServicesTableLib
DebugLib
MemoryAllocationLib
BaseMemoryLib
DevicePathLib
UefiLib
HobLib
[Protocols]
gEfiPciPlatformProtocolGuid ## PRODUCES
gEfiPciIoProtocolGuid ## COMSUMES

View File

@@ -568,6 +568,7 @@
MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf
!endif
UefiPayloadPkg/GraphicsOutputDxe/GraphicsOutputDxe.inf
UefiPayloadPkg/PciPlatformDxe/PciPlatformDxe.inf
#
# Random Number Generator

View File

@@ -168,6 +168,8 @@ INF MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf
!endif
INF UefiPayloadPkg/GraphicsOutputDxe/GraphicsOutputDxe.inf
INF UefiPayloadPkg/PciPlatformDxe/PciPlatformDxe.inf
#
# SCSI/ATA/IDE/DISK Support
#