diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
index d18f1f712e..4de018b2a5 100644
--- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
+++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
@@ -2,6 +2,7 @@
# The PCI bus driver will probe all PCI devices and allocate MMIO and IO space for these devices.
# Please use PCD feature flag PcdPciBusHotplugDeviceSupport to enable hot plug supporting.
#
+# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -97,6 +98,8 @@
gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration ## SOMETIMES_CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdPcieResizableBarSupport ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdPcieResizableBarMaxSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdPciCrsRetryIntervalUs ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdPciCrsTimeoutSeconds ## CONSUMES
[UserExtensions.TianoCore."ExtraFiles"]
PciBusDxeExtra.uni
diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c
index 278a0eb9c4..bd5dd71fb3 100644
--- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c
+++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c
@@ -1,6 +1,7 @@
/** @file
PCI emumeration support functions implementation for PCI Bus module.
+Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
(C) Copyright 2015 Hewlett Packard Enterprise Development LP
Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
@@ -18,6 +19,18 @@ extern EDKII_DEVICE_SECURITY_PROTOCOL *mDeviceSecurityProtocol;
#define SQUAD_ALIGN 0xFFFFFFFFFFFFFFFDULL
#define DQUAD_ALIGN 0xFFFFFFFFFFFFFFFCULL
+//
+// Microseconds per second/millisecond for time conversions
+//
+#define MICROSECONDS_PER_SECOND 1000000
+#define MICROSECONDS_PER_MILLISECOND 1000
+
+//
+// Special PCI Vendor ID values per PCIe Base Specification
+//
+#define PCI_VENDOR_ID_NONE 0xFFFF // No device present
+#define PCI_VENDOR_ID_CRS 0x0001 // Configuration Request Retry Status
+
/**
This routine is used to check whether the pci device is present.
@@ -42,6 +55,27 @@ PciDevicePresent (
{
UINT64 Address;
EFI_STATUS Status;
+ UINTN CrsRetryCount;
+ UINTN CrsRetryIntervalUs;
+ UINTN CrsTimeoutSeconds;
+ UINTN CrsMaxRetries;
+
+ //
+ // Get CRS retry parameters from PCDs
+ //
+ CrsRetryIntervalUs = PcdGet32 (PcdPciCrsRetryIntervalUs);
+ CrsTimeoutSeconds = PcdGet32 (PcdPciCrsTimeoutSeconds);
+
+ //
+ // Calculate max retries. CRS retry is disabled if:
+ // - PcdPciCrsTimeoutSeconds is 0 (default)
+ // - PcdPciCrsRetryIntervalUs is 0 (avoid division by zero)
+ //
+ if ((CrsTimeoutSeconds == 0) || (CrsRetryIntervalUs == 0)) {
+ CrsMaxRetries = 0;
+ } else {
+ CrsMaxRetries = (CrsTimeoutSeconds * MICROSECONDS_PER_SECOND) / CrsRetryIntervalUs;
+ }
//
// Create PCI address map in terms of Bus, Device and Func
@@ -59,17 +93,97 @@ PciDevicePresent (
Pci
);
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_FOUND;
+ }
+
//
- // The host bridge may be programmed to accept Configuration Retry Status (CRS). If the PCI device
- // is slow, and CRS is enabled, the VendorId may read as 0x0001 when not ready.
- // This behavior is defined in PCI spec that VendorId is 0x0001.
- // PCI EXPRESS BASE SPECIFICATION, REV. 3.1 section 2.3.1.
- // Skip the device, as all the other data read will be invalid.
+ // Check for valid device (not absent and not CRS)
//
- if (!EFI_ERROR (Status)) {
- if (((Pci->Hdr).VendorId != 0xffff) && ((Pci->Hdr).VendorId != 0x0001)) {
+ if (((Pci->Hdr).VendorId != PCI_VENDOR_ID_NONE) && ((Pci->Hdr).VendorId != PCI_VENDOR_ID_CRS)) {
+ //
+ // Valid device found - read the entire config header
+ //
+ Status = PciRootBridgeIo->Pci.Read (
+ PciRootBridgeIo,
+ EfiPciWidthUint32,
+ Address,
+ sizeof (PCI_TYPE00) / sizeof (UINT32),
+ Pci
+ );
+
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Check for no device present
+ //
+ if ((Pci->Hdr).VendorId == PCI_VENDOR_ID_NONE) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // CRS detected (Vendor ID = PCI_VENDOR_ID_CRS)
+ // The host bridge may be programmed to accept Configuration Retry Status (CRS).
+ // If the PCI device is slow, and CRS is enabled, the VendorId reads as PCI_VENDOR_ID_CRS.
+ // This behavior is defined in PCI EXPRESS BASE SPECIFICATION, REV. 3.1 section 2.3.1.
+ //
+
+ //
+ // If CRS retry is disabled, skip the device
+ //
+ if (CrsMaxRetries == 0) {
+ DEBUG ((
+ DEBUG_WARN,
+ "PCI %02x:%02x.%x returned CRS but retry is disabled (PcdPciCrsTimeoutSeconds=%d, PcdPciCrsRetryIntervalUs=%d)\n",
+ Bus,
+ Device,
+ Func,
+ CrsTimeoutSeconds,
+ CrsRetryIntervalUs
+ ));
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // CRS retry is enabled - wait for device to become ready
+ //
+ DEBUG ((
+ DEBUG_INFO,
+ "PCI %02x:%02x.%x returned CRS, waiting for device ready (timeout %d s)...\n",
+ Bus,
+ Device,
+ Func,
+ CrsTimeoutSeconds
+ ));
+
+ for (CrsRetryCount = 1; CrsRetryCount <= CrsMaxRetries; CrsRetryCount++) {
+ //
+ // Stall before retry
+ //
+ gBS->Stall (CrsRetryIntervalUs);
+
+ //
+ // Re-read the Vendor ID register
+ //
+ Status = PciRootBridgeIo->Pci.Read (
+ PciRootBridgeIo,
+ EfiPciWidthUint32,
+ Address,
+ 1,
+ Pci
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Check if device is now ready (no longer returning CRS)
+ //
+ if ((Pci->Hdr).VendorId != PCI_VENDOR_ID_CRS) {
//
- // Read the entire config header for the device
+ // Valid device found - read the entire config header
//
Status = PciRootBridgeIo->Pci.Read (
PciRootBridgeIo,
@@ -79,12 +193,32 @@ PciDevicePresent (
Pci
);
+ DEBUG ((
+ DEBUG_INFO,
+ "PCI %02x:%02x.%x ready after %d CRS retries (%d ms)\n",
+ Bus,
+ Device,
+ Func,
+ CrsRetryCount,
+ (CrsRetryCount * CrsRetryIntervalUs) / MICROSECONDS_PER_MILLISECOND
+ ));
+
return EFI_SUCCESS;
- } else if ((Pci->Hdr).VendorId == 0x0001) {
- DEBUG ((DEBUG_WARN, "CRS response detected. Devices that return a CRS response during enumeration are currently ignored\n"));
}
}
+ //
+ // CRS timeout - device never became ready
+ //
+ DEBUG ((
+ DEBUG_ERROR,
+ "PCI %02x:%02x.%x CRS timeout after %d seconds - device not ready\n",
+ Bus,
+ Device,
+ Func,
+ CrsTimeoutSeconds
+ ));
+
return EFI_NOT_FOUND;
}
diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
index 5195ea4aae..aab6022db0 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -3,7 +3,7 @@
# It also provides the definitions(including PPIs/PROTOCOLs/GUIDs and library classes)
# and libraries instances, which are used for those modules.
#
-# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
+# Copyright (c) 2019 - 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# Copyright (c) 2007 - 2024, Intel Corporation. All rights reserved.
# Copyright (c) 2016, Linaro Ltd. All rights reserved.
# (C) Copyright 2016 - 2019 Hewlett Packard Enterprise Development LP
@@ -2325,6 +2325,20 @@
# @Prompt Max size to accept in PCIe Resizable BAR Capability register.
gEfiMdeModulePkgTokenSpaceGuid.PcdPcieResizableBarMaxSize|43|UINT8|0x10000030
+ ## PCIe Configuration Request Retry Status (CRS) retry interval in microseconds.
+ # When a PCIe device returns CRS during enumeration, the PCI bus driver will wait
+ # this interval before retrying. Default is 10000us (10ms) to match Linux kernel.
+ # If set to 0, CRS retry is disabled regardless of PcdPciCrsTimeoutSeconds.
+ # @Prompt PCI CRS retry interval in microseconds.
+ gEfiMdeModulePkgTokenSpaceGuid.PcdPciCrsRetryIntervalUs|10000|UINT32|0x1000002A
+
+ ## PCIe Configuration Request Retry Status (CRS) timeout in seconds.
+ # Maximum time to wait for a device returning CRS to become ready.
+ # When set to 0 (default), devices returning CRS are skipped during enumeration.
+ # Platforms requiring CRS retry support should set a non-zero value.
+ # @Prompt PCI CRS timeout in seconds.
+ gEfiMdeModulePkgTokenSpaceGuid.PcdPciCrsTimeoutSeconds|0|UINT32|0x1000002B
+
[PcdsPatchableInModule]
## Specify memory size with page number for PEI code when
# Loading Module at Fixed Address feature is enabled.