diff --git a/UefiPayloadPkg/PciPlatformDxe/PciPlatformDxe.c b/UefiPayloadPkg/PciPlatformDxe/PciPlatformDxe.c
new file mode 100644
index 0000000000..05b66c0e8c
--- /dev/null
+++ b/UefiPayloadPkg/PciPlatformDxe/PciPlatformDxe.c
@@ -0,0 +1,459 @@
+/** @file
+ Implementation of the gEfiPciPlatformProtocol to support loading
+ PCI Option ROMs when full PCI enumeration is skipped.
+
+Copyright (c) 2025, 9elements GmbH. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+
+**/
+
+#include "PciPlatformDxe.h"
+#include
+#include
+
+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_UNSUPPORTED;
+}
+
+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_UNSUPPORTED;
+}
+
+/**
+ Gets the PCI device's option ROM from a platform-specific location.
+
+ The GetPciRom() function gets the PCI device's option ROM from a platform-specific location.
+ The option ROM will be loaded into memory. This member function is used to return an image
+ that is packaged as a PCI 2.2 option ROM. The image may contain both legacy and EFI option
+ ROMs. See the UEFI 2.0 Specification for details. This member function can be used to return
+ option ROM images for embedded controllers. Option ROMs for embedded controllers are typically
+ stored in platform-specific storage, and this member function can retrieve it from that storage
+ and return it to the PCI bus driver. The PCI bus driver will call this member function before
+ scanning the ROM that is attached to any controller, which allows to scan the PCI card for
+ ROM image and retrieve it.
+
+ @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 PciSegment;
+ UINTN PciBus;
+ UINTN PciDevice;
+ UINTN PciFunction;
+ UINTN RomBarIndex;
+ UINT32 Buffer;
+ UINT32 AllOnes;
+ PCI_IO_DEVICE *PciIoDevice;
+ UINT8 Indicator;
+ UINT16 OffsetPcir;
+ UINT32 RomBarOffset;
+ UINT32 RomBar;
+ BOOLEAN FirstCheck;
+ UINT64 RomImageSize;
+ UINT32 LegacyImageLength;
+ UINT8 *RomInMemory;
+ UINT8 CodeType;
+ PCI_EXPANSION_ROM_HEADER RomHeader;
+ PCI_DATA_STRUCTURE RomPcir;
+
+ //
+ // Check to see if RomImage is NULL
+ //
+ if (RomImage == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check to see if RomSize is NULL
+ //
+ if (RomSize == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *RomImage = NULL;
+ *RomSize = 0;
+
+ Status = gBS->HandleProtocol (
+ PciHandle,
+ &gEfiPciIoProtocolGuid,
+ (VOID **)&PciIo
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Failed to open gEfiPciIoProtocolGuid\n", __func__));
+ return EFI_UNSUPPORTED;
+ }
+
+ PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (PciIo);
+
+ //
+ // Get the location of the PCI device
+ //
+ PciIo->GetLocation (
+ PciIo,
+ &PciSegment,
+ &PciBus,
+ &PciDevice,
+ &PciFunction
+ );
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: PCI @ [%02x|%02x|%02x|%02x] Searching Option ROM...\n",
+ __func__,
+ PciSegment,
+ PciBus,
+ PciDevice,
+ PciFunction
+ ));
+
+ //
+ // Default ROM is at 0x30
+ //
+ RomBarIndex = PCI_EXPANSION_ROM_BASE;
+
+ if (IS_PCI_BRIDGE (&PciIoDevice->Pci)) {
+ //
+ // If is ppb ROM is at 0x38
+ //
+ RomBarIndex = PCI_BRIDGE_ROMBAR;
+ }
+
+ //
+ // Backup ROM BAR
+ //
+ Status = PciIo->Pci.Read (
+ PciIo,
+ EfiPciWidthUint32,
+ RomBarIndex,
+ 1,
+ &Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ goto CloseAndReturn;
+ }
+
+ //
+ // 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)) {
+ goto CloseAndReturn;
+ }
+
+ //
+ // Read back
+ //
+ Status = PciIo->Pci.Read (
+ PciIo,
+ EfiPciWidthUint32,
+ RomBarIndex,
+ 1,
+ &AllOnes
+ );
+ if (EFI_ERROR (Status)) {
+ goto CloseAndReturn;
+ }
+
+ //
+ // Bits [1, 10] are reserved
+ //
+ AllOnes &= 0xFFFFF800;
+ if ((AllOnes == 0) || (AllOnes == 0xFFFFF800)) {
+ DEBUG ((DEBUG_VERBOSE, "%a: No Option ROM found\n", __func__));
+ return EFI_NOT_FOUND;
+ }
+
+ *RomSize = (~AllOnes) + 1;
+
+ //
+ // Restore BAR and enable it
+ //
+ RomBar = Buffer | 1;
+ Status = PciIo->Pci.Write (
+ PciIo,
+ EfiPciWidthUint32,
+ RomBarIndex,
+ 1,
+ &RomBar
+ );
+ if (EFI_ERROR (Status)) {
+ goto CloseAndReturn;
+ }
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: ROM BAR enabled at 0x%x with size %d\n",
+ __func__,
+ Buffer &~1,
+ *RomSize
+ ));
+
+ //
+ // Set pointer to ROM BAR
+ //
+ RomBar = Buffer & 0xFFFFF800;
+
+ RomBarOffset = RomBar;
+ FirstCheck = TRUE;
+ LegacyImageLength = 0;
+ RomImageSize = 0;
+
+ //
+ // Expect to find a PCI ROM header at offset 0.
+ // The ROM header contains a pointer to the PCI Data structure.
+ // The PCI Data structure holds the image size.
+ // At the end of the image another Option ROM might be found
+ // (one for legacy BIOS and one for EFI).
+ //
+ do {
+ Status = PciIoDevice->PciRootBridgeIo->Mem.Read (
+ PciIoDevice->PciRootBridgeIo,
+ EfiPciWidthUint8,
+ RomBarOffset,
+ sizeof (PCI_EXPANSION_ROM_HEADER),
+ (UINT8 *)&RomHeader
+ );
+ if (EFI_ERROR (Status)) {
+ goto RestoreBar;
+ }
+
+ 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;
+ }
+
+ Status = PciIoDevice->PciRootBridgeIo->Mem.Read (
+ PciIoDevice->PciRootBridgeIo,
+ EfiPciWidthUint8,
+ RomBarOffset + OffsetPcir,
+ sizeof (PCI_DATA_STRUCTURE),
+ (UINT8 *)&RomPcir
+ );
+ if (EFI_ERROR (Status)) {
+ goto RestoreBar;
+ }
+
+ //
+ // 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) {
+ //
+ // Allocate buffer to return
+ //
+ RomInMemory = (UINT8 *)AllocatePool ((UINT32)RomImageSize);
+ if (RomInMemory == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto RestoreBar;
+ }
+
+ //
+ // Copy ROM image into memory
+ //
+ Status = PciIoDevice->PciRootBridgeIo->Mem.Read (
+ PciIoDevice->PciRootBridgeIo,
+ EfiPciWidthUint8,
+ RomBar,
+ (UINT32)RomImageSize,
+ RomInMemory
+ );
+ if (EFI_ERROR (Status)) {
+ goto RestoreBar;
+ }
+ } else {
+ Status = EFI_NOT_FOUND;
+ goto RestoreBar;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: PCI @ [%02x|%02x|%02x|%02x]: Found Option ROM at 0x%x, length 0x%x\n",
+ __func__,
+ PciSegment,
+ PciBus,
+ PciDevice,
+ PciFunction,
+ RomBar,
+ RomImageSize
+ ));
+
+ PciIoDevice->EmbeddedRom = TRUE;
+ PciIoDevice->PciIo.RomSize = RomImageSize;
+ PciIoDevice->PciIo.RomImage = RomInMemory;
+
+ *RomImage = RomInMemory;
+ *RomSize = RomImageSize;
+
+ Status = EFI_SUCCESS;
+
+RestoreBar:
+ //
+ // Restore BAR
+ //
+ PciIo->Pci.Write (
+ PciIo,
+ EfiPciWidthUint32,
+ RomBarIndex,
+ 1,
+ &RomBar
+ );
+
+CloseAndReturn:
+ //
+ // Close the I/O Abstraction(s) used to perform the supported test
+ //
+ gBS->CloseProtocol (
+ PciHandle,
+ &gEfiPciIoProtocolGuid,
+ PciIo,
+ PciHandle
+ );
+
+ return Status;
+}
+
+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;
+ EFI_HANDLE DriverHandle = NULL;
+
+ Status = gBS->InstallProtocolInterface (
+ &DriverHandle,
+ &gEfiPciPlatformProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &mPciPlatformProtocol
+ );
+
+ return Status;
+}
diff --git a/UefiPayloadPkg/PciPlatformDxe/PciPlatformDxe.h b/UefiPayloadPkg/PciPlatformDxe/PciPlatformDxe.h
new file mode 100644
index 0000000000..322fc5f8db
--- /dev/null
+++ b/UefiPayloadPkg/PciPlatformDxe/PciPlatformDxe.h
@@ -0,0 +1,20 @@
+/** @file
+ Header file for a gEfiPciPlatformProtocol driver.
+
+Copyright (c) 2025, 9elements GmbH. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+
+**/
+
+#ifndef _PCI_PLATFORM_DXE_H_
+#define _PCI_PLATFORM_DXE_H_
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#endif
diff --git a/UefiPayloadPkg/PciPlatformDxe/PciPlatformDxe.inf b/UefiPayloadPkg/PciPlatformDxe/PciPlatformDxe.inf
new file mode 100644
index 0000000000..9e0ba63de3
--- /dev/null
+++ b/UefiPayloadPkg/PciPlatformDxe/PciPlatformDxe.inf
@@ -0,0 +1,43 @@
+## @file
+# This driver produces gEfiPciPlatformProtocol to load PCI Option ROMs
+#
+# Copyright (c) 2025, 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 ARM AARCH64
+#
+
+[Sources.common]
+ PciPlatformDxe.h
+ PciPlatformDxe.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ UefiPayloadPkg/UefiPayloadPkg.dec
+
+[LibraryClasses]
+ UefiDriverEntryPoint
+ DxeServicesTableLib
+ DebugLib
+ MemoryAllocationLib
+ BaseMemoryLib
+
+[Protocols]
+ gEfiPciPlatformProtocolGuid ## PRODUCES
+ gEfiPciIoProtocolGuid ## COMSUMES
diff --git a/UefiPayloadPkg/UefiPayloadPkg.dsc b/UefiPayloadPkg/UefiPayloadPkg.dsc
index 34de87f7a5..76e4e58f50 100644
--- a/UefiPayloadPkg/UefiPayloadPkg.dsc
+++ b/UefiPayloadPkg/UefiPayloadPkg.dsc
@@ -45,6 +45,7 @@
DEFINE NVME_ENABLE = TRUE
DEFINE CAPSULE_SUPPORT = FALSE
DEFINE LOCKBOX_SUPPORT = FALSE
+ DEFINE LOAD_OPTION_ROMS = FALSE
#
# Crypto Support
@@ -1035,6 +1036,13 @@
MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf
!endif
+ #
+ # Support for loading Option ROMs from PCI-Express devices
+ #
+!if $(LOAD_OPTION_ROMS) == TRUE
+ UefiPayloadPkg/PciPlatformDxe/PciPlatformDxe.inf
+!endif
+
#
# Usb Support
#
diff --git a/UefiPayloadPkg/UefiPayloadPkg.fdf b/UefiPayloadPkg/UefiPayloadPkg.fdf
index 4264fa9a0e..06ca20761a 100644
--- a/UefiPayloadPkg/UefiPayloadPkg.fdf
+++ b/UefiPayloadPkg/UefiPayloadPkg.fdf
@@ -292,6 +292,10 @@ INF MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf
!endif
INF UefiPayloadPkg/GraphicsOutputDxe/GraphicsOutputDxe.inf
+!if $(LOAD_OPTION_ROMS) == TRUE
+INF UefiPayloadPkg/PciPlatformDxe/PciPlatformDxe.inf
+!endif
+
#
# SCSI/ATA/IDE/DISK Support
#