From: Mario Limonciello <mario.limonciello@amd.com>
To: Oz Tiram <oz@shift-computing.de>, amd-gfx@lists.freedesktop.org
Cc: alexander.deucher@amd.com, christian.koenig@amd.com,
airlied@gmail.com, simona@ffwll.ch,
dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2] drm/amd/amdgpu: add firmware file fallback for APU VBIOS discovery
Date: Sun, 5 Jul 2026 19:56:25 -0500 [thread overview]
Message-ID: <bd1f1632-f53d-4bfd-9d28-5b66e466366c@amd.com> (raw)
In-Reply-To: <42e07bbb-9384-4871-a345-e0fd6c00a772@shift-computing.de>
[-- Attachment #1: Type: text/plain, Size: 5984 bytes --]
On 7/5/26 14:10, Oz Tiram wrote:
> Hi Mario,
>
> To make sure I understand correctly: are you suggesting that the bus
> number in the VFCT was legitimate at BIOS POST time, and that
> pci=realloc,assign-busses is what changes it at runtime, causing the
> mismatch?
That's what it sounds like right now. You can easily drop all the
superfluous kernel command line optiosn and see.
>
> I'm not familiar enough with the PCI subsystem to know the right way to
> implement that — could you point me in the right direction?
Well there's a variety of ways to do it. But how about we start here -
if we make that specific busnr match optional and instead make a VID/DID
match.
See if the attached patch helps.
>
> Oz
>
> On 7/5/26 20:37, Mario Limonciello wrote:
>>
>>
>> On 7/5/26 05:04, Oz Tiram wrote:
>>> APUs (e.g. AMD Radeon 780M / HawkPoint, PCI 1002:1900) have no
>>> dedicated VBIOS ROM chip. amdgpu_get_bios_apu() attempts four paths
>>> before giving up:
>>>
>>> 1. ACPI VFCT table
>>> 2. VRAM BAR read
>>> 3. ROM BAR read
>>> 4. platform BIOS
>>>
>>> On some systems all four fail. The specific case motivating this patch
>>> is a hybrid graphics machine (dGPU + APU) where:
>>>
>>> - The VFCT table contains the iGPU entry but with a stale PCIBus
>>> value
>>> from BIOS POST time (0x6A). When the kernel boots with
>>> pci=realloc,assign-busses, PCI bus numbers are reassigned
>>> dynamically
>>> and the iGPU lands on bus 0x0B at runtime. amdgpu_acpi_vfct_bios()
>>> matches entries by bus number, so the entry is never found.
>>> - The VRAM BAR is unmapped at probe time.
>>> - The ROM BAR is zero (PCI firmware did not assign it).
>>> - No platform BIOS mapping exists.
>>>
>>> The UEFI GOP driver initialises the iGPU successfully for early display,
>>> confirming the hardware is functional. The VBIOS image data embedded in
>>> the VFCT is also valid; only the PCIBus metadata is wrong.
>>
>> So the BIOS on this machine is actually totally fine; it's just when
>> the kernel is booted to reassign busses there is a problem?
>>
>> In that case; why not detect the kernel was booted this way and keep
>> track of the original bus number when reassigned to avoid the issue?
>>
>>> The firmware
>>> file can be extracted directly from the VFCT using dd:
>>>
>>> dd if=/sys/firmware/acpi/tables/VFCT bs=1 skip=$((0x68))
>>> count=16896 \
>>> of=/lib/firmware/amdgpu/1002_1900.bin
>>>
>>> (0x68 is the byte offset of the VBIOS image after the ACPI table header
>>> and VFCT_IMAGE_HEADER; the image length 16896 comes from the ImageLength
>>> field in VFCT_IMAGE_HEADER.)
>>>
>>> The driver then prints "Unable to locate a BIOS ROM" and refuses to
>>> bind, leaving the APU completely unusable under Linux.
>>>
>>> Add a fifth fallback: request a firmware file named
>>> "amdgpu/<vendor>_<device>.bin" (e.g. "amdgpu/1002_1900.bin") via
>>> request_firmware(). This allows a VBIOS image extracted as above to be
>>> placed in /lib/firmware/ and makes the binding succeed without patching
>>> ACPI tables or BIOS.
>>>
>>> The fallback is only reached if all existing paths have already failed,
>>> so there is no regression risk for boards where VFCT or ROM BAR work.
>>
>> What happens if the VBIOS changes in another way one boot to another?
>> You might have some other stateful information that isn't updated.
>>
>> The whole thing to me feels like a hack for a behavior we can control
>> in the kernel when doing reassignments.
>>>
>>> Signed-off-by: Oz Tiram <oz@shift-computing.de>
>>> ---
>>> v2: Fix commit message: clarify that VFCT contains the iGPU entry but
>>> with a stale PCIBus from BIOS POST that mismatches the runtime bus
>>> number assigned by pci=realloc,assign-busses. Explain that the
>>> VBIOS
>>> image data is valid and document the dd extraction command and byte
>>> offsets. Note that the UEFI GOP driver initialises the iGPU
>>> successfully, confirming the hardware is functional.
>>>
>>> drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c | 23 +++++++++++++++++++++++
>>> 1 file changed, 23 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/
>>> drm/amd/amdgpu/amdgpu_bios.c
>>> index aa039e148a5e..86064c753b09 100644
>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
>>> @@ -26,6 +26,7 @@
>>> * Jerome Glisse
>>> */
>>> +#include <linux/firmware.h>
>>> #include "amdgpu.h"
>>> #include "atom.h"
>>> @@ -457,6 +458,28 @@ static bool amdgpu_get_bios_apu(struct
>>> amdgpu_device *adev)
>>> goto success;
>>> }
>>> + {
>>> + const struct firmware *fw;
>>> + char fw_name[32];
>>> + size_t fw_size;
>>> +
>>> + snprintf(fw_name, sizeof(fw_name), "amdgpu/%04x_%04x.bin",
>>> + adev->pdev->vendor, adev->pdev->device);
>>> + if (request_firmware(&fw, fw_name, adev->dev) == 0) {
>>> + adev->bios = kmemdup(fw->data, fw->size, GFP_KERNEL);
>>> + fw_size = fw->size;
>>> + release_firmware(fw);
>>> + if (!adev->bios || !check_atom_bios(adev, fw_size)) {
>>> + amdgpu_bios_release(adev);
>>> + } else {
>>> + adev->bios_size = fw_size;
>>> + dev_info(adev->dev, "Fetched VBIOS from firmware
>>> file %s\n",
>>> + fw_name);
>>> + goto success;
>>> + }
>>> + }
>>> + }
>>> +
>>> dev_err(adev->dev, "Unable to locate a BIOS ROM\n");
>>> return false;
>>
[-- Attachment #2: 0001-drm-amdgpu-radeon-Fix-VFCT-bus-number-matching-with-.patch --]
[-- Type: text/x-patch, Size: 8107 bytes --]
From 42ac2fe3085cc3593523ac3e514bc36616078b4f Mon Sep 17 00:00:00 2001
From: Mario Limonciello <mario.limonciello@amd.com>
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 <mario.limonciello@amd.com>
---
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
next prev parent reply other threads:[~2026-07-06 0:56 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-21 17:32 [PATCH] " Oz Tiram
2026-06-21 18:01 ` Oz Tiram
2026-06-26 17:42 ` Mario Limonciello
2026-06-26 19:38 ` Alex Deucher
2026-07-05 10:04 ` [PATCH v2] " Oz Tiram
2026-07-05 18:37 ` Mario Limonciello
2026-07-05 19:10 ` Oz Tiram
2026-07-06 0:56 ` Mario Limonciello [this message]
2026-07-08 12:36 ` Oz Tiram
2026-07-08 12:55 ` Mario Limonciello
[not found] ` <cc849fb3-224e-43c0-bc50-67fd025009e7@shift-computing.de>
2026-07-08 13:13 ` Mario Limonciello
2026-07-08 16:35 ` Oz Tiram
2026-07-08 16:39 ` Mario Limonciello
2026-07-08 17:53 ` Oz Tiram
[not found] ` <d224d046-0480-4e38-9e93-29a0d37f3331@shift-computing.de>
2026-07-08 17:59 ` Mario Limonciello
2026-07-08 18:02 ` Alex Deucher
2026-07-08 18:03 ` Mario Limonciello
2026-07-08 18:12 ` Alex Deucher
2026-07-05 10:15 ` [PATCH] " Oz Tiram
[not found] <CAGxFSwnJYc03fN+XWU8oLW9EbXhmetQuj1s3-m8UdZHoBdGJ8Q@mail.gmail.com>
2026-11-02 14:59 ` [PATCH v2] " Mario Limonciello
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=bd1f1632-f53d-4bfd-9d28-5b66e466366c@amd.com \
--to=mario.limonciello@amd.com \
--cc=airlied@gmail.com \
--cc=alexander.deucher@amd.com \
--cc=amd-gfx@lists.freedesktop.org \
--cc=christian.koenig@amd.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=linux-kernel@vger.kernel.org \
--cc=oz@shift-computing.de \
--cc=simona@ffwll.ch \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®