@@ -215,6 +215,7 @@ void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev);
bool vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp);
void vfio_quirk_reset(VFIOPCIDevice *vdev);
VFIOQuirk *vfio_quirk_alloc(int nr_mem);
+void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr);
void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr);
extern const PropertyInfo qdev_prop_nv_gpudirect_clique;
@@ -374,6 +374,104 @@ static const MemoryRegionOps vfio_igd_index_quirk = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
+#define IGD_BDSM_MMIO_OFFSET 0x1080C0
+
+static uint64_t vfio_igd_quirk_bdsm_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOPCIDevice *vdev = opaque;
+ uint64_t offset;
+
+ offset = IGD_BDSM_GEN11 + addr;
+
+ switch (size) {
+ case 1:
+ return pci_get_byte(vdev->pdev.config + offset);
+ case 2:
+ return pci_get_word(vdev->pdev.config + offset);
+ case 4:
+ return pci_get_long(vdev->pdev.config + offset);
+ case 8:
+ return pci_get_quad(vdev->pdev.config + offset);
+ default:
+ hw_error("igd: unsupported read size, %u bytes", size);
+ break;
+ }
+
+ return 0;
+}
+
+static void vfio_igd_quirk_bdsm_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOPCIDevice *vdev = opaque;
+ uint64_t offset;
+
+ offset = IGD_BDSM_GEN11 + addr;
+
+ switch (size) {
+ case 1:
+ pci_set_byte(vdev->pdev.config + offset, data);
+ break;
+ case 2:
+ pci_set_word(vdev->pdev.config + offset, data);
+ break;
+ case 4:
+ pci_set_long(vdev->pdev.config + offset, data);
+ break;
+ case 8:
+ pci_set_quad(vdev->pdev.config + offset, data);
+ break;
+ default:
+ hw_error("igd: unsupported read size, %u bytes", size);
+ break;
+ }
+}
+
+static const MemoryRegionOps vfio_igd_bdsm_quirk = {
+ .read = vfio_igd_quirk_bdsm_read,
+ .write = vfio_igd_quirk_bdsm_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOQuirk *quirk;
+ int gen;
+
+ /*
+ * This must be an Intel VGA device at address 00:02.0 for us to even
+ * consider enabling legacy mode. Some driver have dependencies on the PCI
+ * bus address.
+ */
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) ||
+ !vfio_is_vga(vdev) || nr != 0 ||
+ &vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev),
+ 0, PCI_DEVFN(0x2, 0))) {
+ return;
+ }
+
+ /*
+ * Only on IGD devices of gen 11 and above, the BDSM register is mirrored
+ * into MMIO space and read from MMIO space by the Windows driver.
+ */
+ gen = igd_gen(vdev);
+ if (gen < 11) {
+ return;
+ }
+
+ quirk = vfio_quirk_alloc(1);
+ quirk->data = vdev;
+
+ memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_igd_bdsm_quirk,
+ vdev, "vfio-igd-bdsm-quirk", 8);
+ memory_region_add_subregion_overlap(vdev->bars[0].region.mem,
+ IGD_BDSM_MMIO_OFFSET, &quirk->mem[0],
+ 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+}
+
void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
{
g_autofree struct vfio_region_info *rom = NULL;
@@ -1259,6 +1259,7 @@ void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr)
vfio_probe_nvidia_bar0_quirk(vdev, nr);
vfio_probe_rtl8168_bar2_quirk(vdev, nr);
#ifdef CONFIG_VFIO_IGD
+ vfio_probe_igd_bar0_quirk(vdev, nr);
vfio_probe_igd_bar4_quirk(vdev, nr);
#endif
}