diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 0b6533bbf7..0d68c6a451 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -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; diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 39dae72497..d37f722cce 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -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 } diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index bf67df2fbc..5ad090a229 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -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;