Hi, as these patches fixes legacy mode grabled grub screen they add
regression to UEFI guest where monitor won't woke up until grub timeout.

CMD used : -device
vfio-pci,host=00:02.0,id=igpu,addr=2.0,x-igd-legacy-mode=off,x-igd-lpc=on,romfile=/home/maan/QemuVMs/MyGopRomfiles/InteligdGopHD4600.rom

romfile is patched with intelgopdriver.efi from VfioIgdPkg repo.

Here is what i915 logs say in guest with patched qemu :
[   10.823656] i915 0000:00:02.0: [drm] Found haswell (device ID 0412)
integrated display version 7.00 stepping N/A
[   10.824300] i915 0000:00:02.0: [drm] VT-d active for gfx access
[   10.824763] i915 0000:00:02.0: vgaarb: deactivate vga console
[   10.824867] i915 0000:00:02.0: [drm] Using Transparent Hugepages
[   10.824871] i915 0000:00:02.0: [drm] DMAR active, disabling use of
stolen memory
[   10.832111] i915 0000:00:02.0: Invalid PCI ROM header signature:
expecting 0xaa55, got 0x0000
[   10.832118] i915 0000:00:02.0: [drm] Failed to find VBIOS tables (VBT)
[   10.832154] i915 0000:00:02.0: vgaarb: VGA decodes changed:
olddecodes=io+mem,decodes=io+mem:owns=io+mem
[   10.842837] i915 0000:00:02.0: [drm] Registered 3 planes with drm panic
[   10.850377] [drm] Initialized i915 1.6.0 for 0000:00:02.0 on minor 0
[   11.678013] fbcon: i915drmfb (fb0) is primary device
[   11.748610] i915 0000:00:02.0: [drm] fb0: i915drmfb frame buffer device

So look like VBT table is not expose to guest.

These are the logs of stock qemu with working grub and all :
[   28.945622] i915 0000:00:02.0: [drm] Found haswell (device ID 0412)
integrated display version 7.00 stepping N/A
[   28.946393] i915 0000:00:02.0: [drm] VT-d active for gfx access
[   28.961770] i915 0000:00:02.0: vgaarb: deactivate vga console
[   28.961830] i915 0000:00:02.0: [drm] Using Transparent Hugepages
[   28.961833] i915 0000:00:02.0: [drm] DMAR active, disabling use of
stolen memory
[   29.016673] i915 0000:00:02.0: vgaarb: VGA decodes changed:
olddecodes=io+mem,decodes=io+mem:owns=io+mem
[   29.051910] i915 0000:00:02.0: [drm] Registered 3 planes with drm panic
[   29.055360] [drm] Initialized i915 1.6.0 for 0000:00:02.0 on minor 1
[   29.903202] fbcon: i915drmfb (fb0) is primary device
[   29.990056] i915 0000:00:02.0: [drm] fb0: i915drmfb frame buffer device

Regards,
K S Maan

On Sat, 6 Jun 2026 at 03:32, Alex Williamson <[email protected]> wrote:

> On Thu,  4 Jun 2026 01:33:54 +0800
> Tomita Moeko <[email protected]> wrote:
> > diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c
> > index 17437ae18d..e00f6f8315 100644
> > --- a/hw/vfio/igd.c
> > +++ b/hw/vfio/igd.c
> > @@ -739,3 +739,109 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice
> *vdev, Error **errp)
> >
> >      return vfio_pci_igd_config_quirk(vdev, errp);
> >  }
> > +
> > +/*
> > + * IGD ROM BAR read from kernel is actually the host VBIOS shadow RAM
> region,
> > + * which contains host modifications. In Gen 6-9 VBIOS, the routine
> below is
> > + * used to get BDSM value when programming the initial GTT.
> > + *   xx xx xx xx           v: .long ?                  # saved value
> > + *   66 53                    push  %ebx
> > + *   66 2e 83 3e xx xx 00     cmpl  $0x0,%cs:v         # is saved value
> empty?
> > + *   74 07                    je    1f                 # if zero, go
> compute
> > + *   66 2e a1 xx xx           mov   %cs:v,%eax         # else return
> saved value
> > + *   eb 0f                    jmp   2f
> > + *   b8 5e 10              1: mov   $0x105e,%ax        # dev 00:02.0,
> offset 5E
> > + *   e8 xx xx                 call  pci_read_cfg_word
> > + *   66 c1 e0 10              shl   $0x10,%eax         # left shift 16
> bits
> > + *   66 2e a3 xx xx           mov   %eax,%cs:v         # save the result
> > + *   66 5b                 2:pop   %ebx
> > + *   c3                       ret
> > + * When running the VBIOS in guest, saved value still reflects the host
> stolen
> > + * memory base address, which is not correct in guest. So we need to
> patch the
> > + * VBIOS to clear the saved value.
> > + *
> > + * The unique 19-byte starts at `cmpl $0,%cs:v` and ends at `mov
> $0x105e,%ax`
> > + * anchors the match to the routine. Both `cs:` displacements must
> reference
> > + * the same offset.
> > + */
>
> Neat, cool quirk.
>
> > +static int igd_vbios_find_saved_bdsm(const uint8_t *rom, size_t
> rom_size,
> > +                                     uint16_t *bdsm_offset)
> > +{
> > +    static const uint8_t start[] = { 0x66, 0x2e, 0x83, 0x3e };
> > +    static const uint8_t middle[] = { 0x00, 0x74, 0x07, 0x66, 0x2e,
> 0xa1 };
> > +    static const uint8_t end[] = { 0xeb, 0x0f, 0xb8, 0x5e, 0x10 };
> > +    size_t i;
> > +    bool found = false;
> > +
> > +    if (rom_size < 19) {
> > +        return -ENOENT;
> > +    }
> > +
> > +    for (i = 0; i + 19 <= rom_size; i++) {
> > +        if (memcmp(rom + i, start, sizeof(start)) != 0 ||
> > +            memcmp(rom + i + 6, middle, sizeof(middle)) != 0 ||
> > +            memcmp(rom + i + 14, end, sizeof(end)) != 0) {
> > +            continue;
> > +        }
> > +
> > +        /* same saved value address? */
> > +        if (rom[i + 4] != rom[i + 12] || rom[i + 5] != rom[i + 13]) {
> > +            continue;
> > +        }
> > +
> > +        if (found) {
> > +            return -EEXIST;
> > +        }
> > +
> > +        *bdsm_offset = rom[i + 4] | ((uint16_t)rom[i + 5] << 8);
>
> This needs a bounds check to make sure it's still within the ROM,
> either here or before the memset().
>
> > +        found = true;
> > +    }
> > +
> > +    if (!found) {
> > +        return -ENOENT;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +void vfio_probe_igd_legacy_rom_quirk(VFIOPCIDevice *vdev)
> > +{
> > +    int ret, gen;
> > +    uint16_t pcir_offset, bdsm_offset = 0;
> > +    uint8_t checksum;
> > +
> > +    if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) ||
> > +        !vfio_is_vga(vdev) || !vdev->vga) {
> > +        return;
> > +    }
> > +
> > +    /* Only Gen 6~9 devices have legacy VBIOS as Option ROM */
> > +    gen = igd_gen(vdev);
> > +    if (gen < 6 || gen > 9) {
> > +        return;
> > +    }
> > +
> > +    if (pci_get_word(vdev->rom) != 0xaa55) {
> > +        return;
> > +    }
> > +
> > +    /* Must be a legacy ROM */
> > +    pcir_offset = pci_get_word(vdev->rom + 0x18);
> > +    if (pcir_offset >= vdev->rom_size ||
>
> This gates the below test to pcir_offset + 0x14, so should be:
>
>        if (pcir_offset + 0x14 >= vdev->rom_size ||
>
> > +        memcmp(vdev->rom + pcir_offset, "PCIR", 4) ||
> > +        pci_get_byte(vdev->rom + pcir_offset + 0x14) != 0x00) {
> > +        return;
> > +    }
> > +
> > +    ret = igd_vbios_find_saved_bdsm(vdev->rom, vdev->rom_size,
> &bdsm_offset);
> > +    if (ret < 0) {
> > +        return;
> > +    }
> > +
> > +    memset(vdev->rom + bdsm_offset, 0, sizeof(uint32_t));
> > +
> > +    checksum = pci_rom_calculate_checksum(vdev->rom, vdev->rom_size);
> > +    ((uint8_t *)vdev->rom)[6] = checksum;
>
> If updated per previous recommendation, this becomes:
>
>         ((uint8_t *)vdev->rom)[6] -= checksum;
>
> Thanks,
> Alex
>

Reply via email to