When multiple cards share the same PCI device ID (e.g. RX 6600 and
RX 6600 XT both use 0x73ff), the VFCT table contains separate VBIOS
images for each card distinguished by their bus number.

The current implementation iterates VFCT entries and returns on the
first device-ID match, which may not have an exact bus match. This
causes cards to load the wrong VBIOS image (e.g. RX 6600 loading
RX 6600 XT's VBIOS), resulting in incorrect clock speeds and
potential system hangs.

Fix: perform two passes over the VFCT table. First pass looks for
an exact bus match. If no exact match is found, second pass falls
back to device-identity matching (vendor + device + slot +
function), which handles PCI bus renumbering cases.

This ensures exact bus matches are preferred while still supporting
the device-identity fallback for systems where the kernel
renumbers PCI buses.

---
 drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c | 40 ++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
index f9315e0472fe..0cee0872100a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
@@ -474,6 +474,8 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device 
*adev)
        UEFI_ACPI_VFCT *vfct;
        unsigned int offset;
        bool r = false;
+       GOP_VBIOS_CONTENT *fallback_vbios = NULL;
+       VFCT_IMAGE_HEADER *fallback_vhdr = NULL;

        if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr)))
                return false;
@@ -486,6 +488,44 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device 
*adev)
        vfct = (UEFI_ACPI_VFCT *)hdr;
        offset = vfct->VBIOSImageOffset;

+       /* First pass: look for exact bus match */
+       while (offset < tbl_size) {
+               GOP_VBIOS_CONTENT *vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + 
offset);
+               VFCT_IMAGE_HEADER *vhdr = &vbios->VbiosHeader;
+
+               offset += sizeof(VFCT_IMAGE_HEADER);
+               if (offset > tbl_size) {
+                       dev_info(adev->dev, "ACPI VFCT image header 
truncated,skipping\n");
+                       goto out;
+               }
+
+               offset += vhdr->ImageLength;
+               if (offset > tbl_size) {
+                       dev_info(adev->dev, "ACPI VFCT image 
truncated,skipping\n");
+                       goto out;
+               }
+
+               if (vhdr->ImageLength && vhdr->PCIBus == 
adev->pdev->bus->number &&
+                   vhdr->VendorID == adev->pdev->vendor &&
+                   vhdr->DeviceID == adev->pdev->device &&
+                   vhdr->PCIDevice == PCI_SLOT(adev->pdev->devfn) &&
+                   vhdr->PCIFunction == PCI_FUNC(adev->pdev->devfn)) {
+                       adev->bios = kmemdup(&vbios->VbiosContent,
+                                            vhdr->ImageLength,
+                                            GFP_KERNEL);
+
+                       if (!check_atom_bios(adev, vhdr->ImageLength)) {
+                               amdgpu_bios_release(adev);
+                               goto out;
+                       }
+                       adev->bios_size = vhdr->ImageLength;
+                       r = true;
+                       goto out;
+               }
+       }
+
+       /* Second pass: fall back to device-identity matching */
+       offset = vfct->VBIOSImageOffset;
        while (offset < tbl_size) {
                GOP_VBIOS_CONTENT *vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + 
offset);
                VFCT_IMAGE_HEADER *vhdr = &vbios->VbiosHeader;
--
2.56.0

Reply via email to