Found the root cause is the vendor/device id fixup in option rom
header. EFI requires the signature in ROM header at 0x04 being
0x00000ef1, the new pci_patch_ids() breaks it as it overwrites
checksum at 0x06. Given that EFI does not require either the IDs
in header nor the checksum, will skip the fixup for EFI roms in
v2.

Thanks,
Moeko

On 2026-06-08 10:17, Kirandeep Maan wrote:
> 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] 
> <mailto:[email protected]>> wrote:
> 
>     On Thu,  4 Jun 2026 01:33:54 +0800
>     Tomita Moeko <[email protected] <mailto:[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