From 42ac2fe3085cc3593523ac3e514bc36616078b4f Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Sun, 5 Jul 2026 15:48:05 -0500 Subject: [PATCH] drm/amdgpu/radeon: Fix VFCT bus number matching with soft filter On systems where PCI bus renumbering occurs (e.g. pci=realloc, resource conflicts), the runtime bus number may differ from the BIOS POST bus number recorded in the VFCT table. This causes amdgpu_acpi_vfct_bios() to fail finding the VBIOS even though the correct device entry exists. Introduce amdgpu_acpi_vfct_match() which treats the bus number as a soft filter: vendor/device/function identity is the hard requirement, while exact bus match is the preferred path. When bus numbers disagree but device identity matches, accept the VFCT entry and log a dev_notice for diagnostics. Apply the same fix to radeon_acpi_vfct_bios(). While updating radeon, also modernize the error handling: - Replace DRM_ERROR with dev_info/dev_notice - Remove legacy goto out + acpi_put_table pattern (acpi_put_table was removed from the kernel) - Use early return instead of goto for cleaner control flow Signed-off-by: Mario Limonciello --- drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c | 48 +++++++++++++-- drivers/gpu/drm/radeon/radeon_bios.c | 78 +++++++++++++++++------- 2 files changed, 99 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c index 35d04e69aec0..fa730af5e979 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c @@ -370,6 +370,46 @@ static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev) false : amdgpu_asic_read_disabled_bios(adev); } +/** + * amdgpu_acpi_vfct_match() - Check if a VFCT entry matches the device + * @adev: AMDGPU device + * @vhdr: VFCT image header to check + * + * VFCT entries contain the PCI bus number as recorded during BIOS POST. + * On systems where the kernel renumbers PCI buses (e.g. pci=realloc or + * resource conflicts), the runtime bus number may differ from the POST + * value. Match by device identity (vendor + device + function) and use + * the bus number as a preference: exact bus match is preferred, but when + * the bus numbers disagree we accept the entry if the device identity + * matches. + * + * Returns: 0 on match, -ENODEV on no match + */ +static int amdgpu_acpi_vfct_match(struct amdgpu_device *adev, + VFCT_IMAGE_HEADER *vhdr) +{ + /* Vendor and device IDs must always match */ + if (vhdr->VendorID != adev->pdev->vendor || + vhdr->DeviceID != adev->pdev->device) + return -ENODEV; + + if (vhdr->PCIDevice != PCI_SLOT(adev->pdev->devfn) || + vhdr->PCIFunction != PCI_FUNC(adev->pdev->devfn)) + return -ENODEV; + + /* Exact bus number match - preferred */ + if (vhdr->PCIBus == adev->pdev->bus->number) + return 0; + + /* Bus mismatch but device identity matches (PCI renumbering case) */ + dev_notice(adev->dev, + "VFCT bus number mismatch: table %u != runtime %u, " + "matching by device identity (vendor 0x%04x device 0x%04x)\\n", + vhdr->PCIBus, adev->pdev->bus->number, + adev->pdev->vendor, adev->pdev->device); + return 0; +} + #ifdef CONFIG_ACPI static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) { @@ -406,11 +446,7 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) } if (vhdr->ImageLength && - vhdr->PCIBus == adev->pdev->bus->number && - vhdr->PCIDevice == PCI_SLOT(adev->pdev->devfn) && - vhdr->PCIFunction == PCI_FUNC(adev->pdev->devfn) && - vhdr->VendorID == adev->pdev->vendor && - vhdr->DeviceID == adev->pdev->device) { + !amdgpu_acpi_vfct_match(adev, vhdr)) { adev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); @@ -424,7 +460,7 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) } } - dev_info(adev->dev, "ACPI VFCT table present but broken (too short #2),skipping\n"); + dev_info(adev->dev, "ACPI VFCT table present but broken (too short #2),skipping\\n"); return false; } #else diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index 3a8c5199a0fe..5938a8ed6e73 100644 --- a/drivers/gpu/drm/radeon/radeon_bios.c +++ b/drivers/gpu/drm/radeon/radeon_bios.c @@ -596,21 +596,60 @@ static bool radeon_read_disabled_bios(struct radeon_device *rdev) return legacy_read_disabled_bios(rdev); } +/** + * radeon_acpi_vfct_match() - Check if a VFCT entry matches the device + * @rdev: Radeon device + * @vhdr: VFCT image header to check + * + * VFCT entries contain the PCI bus number as recorded during BIOS POST. + * On systems where the kernel renumbers PCI buses (e.g. pci=realloc or + * resource conflicts), the runtime bus number may differ from the POST + * value. Match by device identity (vendor + device + function) and use + * the bus number as a preference: exact bus match is preferred, but when + * the bus numbers disagree we accept the entry if the device identity + * matches. + * + * Returns: 0 on match, -ENODEV on no match + */ +static int radeon_acpi_vfct_match(struct radeon_device *rdev, + VFCT_IMAGE_HEADER *vhdr) +{ + /* Vendor and device IDs must always match */ + if (vhdr->VendorID != rdev->pdev->vendor || + vhdr->DeviceID != rdev->pdev->device) + return -ENODEV; + + if (vhdr->PCIDevice != PCI_SLOT(rdev->pdev->devfn) || + vhdr->PCIFunction != PCI_FUNC(rdev->pdev->devfn)) + return -ENODEV; + + /* Exact bus number match - preferred */ + if (vhdr->PCIBus == rdev->pdev->bus->number) + return 0; + + /* Bus mismatch but device identity matches (PCI renumbering case) */ + dev_notice(&rdev->pdev->dev, + "VFCT bus number mismatch: table %u != runtime %u, " + "matching by device identity (vendor 0x%04x device 0x%04x)\n", + vhdr->PCIBus, rdev->pdev->bus->number, + rdev->pdev->vendor, rdev->pdev->device); + return 0; +} + #ifdef CONFIG_ACPI static bool radeon_acpi_vfct_bios(struct radeon_device *rdev) { struct acpi_table_header *hdr; acpi_size tbl_size; UEFI_ACPI_VFCT *vfct; - unsigned offset; - bool r = false; + unsigned int offset; if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr))) return false; tbl_size = hdr->length; if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { - DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n"); - goto out; + dev_info(&rdev->pdev->dev, "ACPI VFCT table present but broken (too short #1),skipping\n"); + return false; } vfct = (UEFI_ACPI_VFCT *)hdr; @@ -622,37 +661,34 @@ static bool radeon_acpi_vfct_bios(struct radeon_device *rdev) offset += sizeof(VFCT_IMAGE_HEADER); if (offset > tbl_size) { - DRM_ERROR("ACPI VFCT image header truncated\n"); - goto out; + dev_info(&rdev->pdev->dev, "ACPI VFCT image header truncated,skipping\n"); + return false; } offset += vhdr->ImageLength; if (offset > tbl_size) { - DRM_ERROR("ACPI VFCT image truncated\n"); - goto out; + dev_info(&rdev->pdev->dev, "ACPI VFCT image truncated,skipping\n"); + return false; } if (vhdr->ImageLength && - vhdr->PCIBus == rdev->pdev->bus->number && - vhdr->PCIDevice == PCI_SLOT(rdev->pdev->devfn) && - vhdr->PCIFunction == PCI_FUNC(rdev->pdev->devfn) && - vhdr->VendorID == rdev->pdev->vendor && - vhdr->DeviceID == rdev->pdev->device) { + !radeon_acpi_vfct_match(rdev, vhdr)) { rdev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); - if (rdev->bios) - r = true; - goto out; + if (!rdev->bios || + rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) { + kfree(rdev->bios); + rdev->bios = NULL; + return false; + } + return true; } } - DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n"); - -out: - acpi_put_table(hdr); - return r; + dev_info(&rdev->pdev->dev, "ACPI VFCT table present but broken (too short #2),skipping\n"); + return false; } #else static inline bool radeon_acpi_vfct_bios(struct radeon_device *rdev) -- 2.53.0