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.