vfio/igd: Decouple common quirks from legacy mode

So far, IGD-specific quirks all require enabling legacy mode, which is
toggled by assigning IGD to 00:02.0. However, some quirks, like the BDSM
and GGC register quirks, should be applied to all supported IGD devices.
A new config option, x-igd-legacy-mode=[on|off|auto], is introduced to
control the legacy mode only quirks. The default value is "auto", which
keeps current behavior that enables legacy mode implicitly and continues
on error when all following conditions are met.
* Machine type is i440fx
* IGD device is at guest BDF 00:02.0

If any one of the conditions above is not met, the default behavior is
equivalent to "off", QEMU will fail immediately if any error occurs.

Users can also use "on" to force enabling legacy mode. It checks if all
the conditions above are met and set up legacy mode. QEMU will also fail
immediately on error in this case.

Additionally, the hotplug check in legacy mode is removed as hotplugging
IGD device is never supported, and it will be checked when enabling the
OpRegion quirk.

Signed-off-by: Tomita Moeko <tomitamoeko@gmail.com>
Reviewed-by: Alex Williamson <alex.williamson@redhat.com>
Tested-by: Alex Williamson <alex.williamson@redhat.com>
Reviewed-by: Corvin Köhne <c.koehne@beckhoff.com>
Link: https://lore.kernel.org/qemu-devel/20250306180131.32970-8-tomitamoeko@gmail.com
[ clg: - Changed warn_report() by info_report() in
         vfio_probe_igd_config_quirk() as suggested by Alex W.
       - Fixed spelling in vfio_probe_igd_config_quirk () ]
Signed-off-by: Cédric Le Goater <clg@redhat.com>
This commit is contained in:
Tomita Moeko 2025-03-07 02:01:27 +08:00 committed by Cédric Le Goater
parent 9267f96ad6
commit 2fedccf03c
3 changed files with 77 additions and 53 deletions

View File

@ -15,6 +15,7 @@
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "hw/boards.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "hw/nvram/fw_cfg.h" #include "hw/nvram/fw_cfg.h"
#include "pci.h" #include "pci.h"
@ -432,9 +433,7 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr)
* bus address. * bus address.
*/ */
if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) ||
!vfio_is_vga(vdev) || nr != 0 || !vfio_is_vga(vdev) || nr != 0) {
&vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev),
0, PCI_DEVFN(0x2, 0))) {
return; return;
} }
@ -482,14 +481,13 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr)
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, bdsm_quirk, next); QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, bdsm_quirk, next);
} }
bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp)
Error **errp G_GNUC_UNUSED)
{ {
g_autofree struct vfio_region_info *rom = NULL;
int ret, gen; int ret, gen;
uint64_t gms_size; uint64_t gms_size;
uint64_t *bdsm_size; uint64_t *bdsm_size;
uint32_t gmch; uint32_t gmch;
bool legacy_mode_enabled = false;
Error *err = NULL; Error *err = NULL;
/* /*
@ -498,9 +496,7 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev,
* PCI bus address. * PCI bus address.
*/ */
if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) ||
!vfio_is_vga(vdev) || !vfio_is_vga(vdev)) {
&vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev),
0, PCI_DEVFN(0x2, 0))) {
return true; return true;
} }
@ -516,56 +512,67 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev,
return true; return true;
} }
/*
* Most of what we're doing here is to enable the ROM to run, so if
* there's no ROM, there's no point in setting up this quirk.
* NB. We only seem to get BIOS ROMs, so a UEFI VM would need CSM support.
*/
ret = vfio_get_region_info(&vdev->vbasedev,
VFIO_PCI_ROM_REGION_INDEX, &rom);
if ((ret || !rom->size) && !vdev->pdev.romfile) {
error_report("IGD device %s has no ROM, legacy mode disabled",
vdev->vbasedev.name);
return true;
}
/*
* Ignore the hotplug corner case, mark the ROM failed, we can't
* create the devices we need for legacy mode in the hotplug scenario.
*/
if (vdev->pdev.qdev.hotplugged) {
error_report("IGD device %s hotplugged, ROM disabled, "
"legacy mode disabled", vdev->vbasedev.name);
vdev->rom_read_failed = true;
return true;
}
gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4);
/* /*
* If IGD VGA Disable is clear (expected) and VGA is not already enabled, * For backward compatibility, enable legacy mode when
* try to enable it. Probably shouldn't be using legacy mode without VGA, * - Machine type is i440fx (pc_piix)
* but also no point in us enabling VGA if disabled in hardware. * - IGD device is at guest BDF 00:02.0
* - Not manually disabled by x-igd-legacy-mode=off
*/ */
if (!(gmch & 0x2) && !vdev->vga && !vfio_populate_vga(vdev, &err)) { if ((vdev->igd_legacy_mode != ON_OFF_AUTO_OFF) &&
error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); !strcmp(MACHINE_GET_CLASS(qdev_get_machine())->family, "pc_piix") &&
error_report("IGD device %s failed to enable VGA access, " (&vdev->pdev == pci_find_device(pci_device_root_bus(&vdev->pdev),
"legacy mode disabled", vdev->vbasedev.name); 0, PCI_DEVFN(0x2, 0)))) {
return true; /*
} * IGD legacy mode requires:
* - VBIOS in ROM BAR or file
* - VGA IO/MMIO ranges are claimed by IGD
* - OpRegion
* - Same LPC bridge and Host bridge VID/DID/SVID/SSID as host
*/
g_autofree struct vfio_region_info *rom = NULL;
/* Setup OpRegion access */ legacy_mode_enabled = true;
if (!vfio_pci_igd_setup_opregion(vdev, &err)) { info_report("IGD legacy mode enabled, "
error_append_hint(&err, "IGD legacy mode disabled\n"); "use x-igd-legacy-mode=off to disable it if unwanted.");
error_report_err(err);
return true;
}
/* Setup LPC bridge / Host bridge PCI IDs */ /*
if (!vfio_pci_igd_setup_lpc_bridge(vdev, &err)) { * Most of what we're doing here is to enable the ROM to run, so if
error_append_hint(&err, "IGD legacy mode disabled\n"); * there's no ROM, there's no point in setting up this quirk.
error_report_err(err); * NB. We only seem to get BIOS ROMs, so UEFI VM would need CSM support.
return true; */
ret = vfio_get_region_info(&vdev->vbasedev,
VFIO_PCI_ROM_REGION_INDEX, &rom);
if ((ret || !rom->size) && !vdev->pdev.romfile) {
error_setg(&err, "Device has no ROM");
goto error;
}
/*
* If IGD VGA Disable is clear (expected) and VGA is not already
* enabled, try to enable it. Probably shouldn't be using legacy mode
* without VGA, but also no point in us enabling VGA if disabled in
* hardware.
*/
if (!(gmch & 0x2) && !vdev->vga && !vfio_populate_vga(vdev, &err)) {
error_setg(&err, "Unable to enable VGA access");
goto error;
}
/* Setup OpRegion access */
if (!vfio_pci_igd_setup_opregion(vdev, &err)) {
goto error;
}
/* Setup LPC bridge / Host bridge PCI IDs */
if (!vfio_pci_igd_setup_lpc_bridge(vdev, &err)) {
goto error;
}
} else if (vdev->igd_legacy_mode == ON_OFF_AUTO_ON) {
error_setg(&err,
"Machine is not i440fx or assigned BDF is not 00:02.0");
goto error;
} }
/* /*
@ -627,4 +634,18 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev,
trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, (gms_size / MiB)); trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, (gms_size / MiB));
return true; return true;
error:
/*
* When legacy mode is implicity enabled, continue on error,
* to keep compatibility
*/
if (legacy_mode_enabled && (vdev->igd_legacy_mode == ON_OFF_AUTO_AUTO)) {
error_report_err(err);
error_report("IGD legacy mode disabled");
return true;
}
error_propagate(errp, err);
return false;
} }

View File

@ -3369,6 +3369,8 @@ static const Property vfio_pci_dev_properties[] = {
VFIO_FEATURE_ENABLE_REQ_BIT, true), VFIO_FEATURE_ENABLE_REQ_BIT, true),
DEFINE_PROP_BIT("x-igd-opregion", VFIOPCIDevice, features, DEFINE_PROP_BIT("x-igd-opregion", VFIOPCIDevice, features,
VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false), VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false),
DEFINE_PROP_ON_OFF_AUTO("x-igd-legacy-mode", VFIOPCIDevice,
igd_legacy_mode, ON_OFF_AUTO_AUTO),
DEFINE_PROP_ON_OFF_AUTO("enable-migration", VFIOPCIDevice, DEFINE_PROP_ON_OFF_AUTO("enable-migration", VFIOPCIDevice,
vbasedev.enable_migration, ON_OFF_AUTO_AUTO), vbasedev.enable_migration, ON_OFF_AUTO_AUTO),
DEFINE_PROP("x-migration-multifd-transfer", VFIOPCIDevice, DEFINE_PROP("x-migration-multifd-transfer", VFIOPCIDevice,

View File

@ -158,6 +158,7 @@ struct VFIOPCIDevice {
uint32_t display_xres; uint32_t display_xres;
uint32_t display_yres; uint32_t display_yres;
int32_t bootindex; int32_t bootindex;
OnOffAuto igd_legacy_mode;
uint32_t igd_gms; uint32_t igd_gms;
OffAutoPCIBAR msix_relo; OffAutoPCIBAR msix_relo;
uint8_t nv_gpudirect_clique; uint8_t nv_gpudirect_clique;