@@ -493,7 +493,7 @@ static void vfio_listener_release(VFIOContainer *container)
memory_listener_unregister(&container->listener);
}
-static struct vfio_info_cap_header *
+struct vfio_info_cap_header *
vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id)
{
struct vfio_info_cap_header *hdr;
@@ -11,9 +11,11 @@
*/
#include "qemu/osdep.h"
+#include "hw/nvram/fw_cfg.h"
#include "pci.h"
#include "trace.h"
#include "qemu/range.h"
+#include "qemu/error-report.h"
/* Use uin32_t for vendor & device so PCI_ANY_ID expands and cannot match hw */
static bool vfio_pci_is(VFIOPCIDevice *vdev, uint32_t vendor, uint32_t device)
@@ -962,6 +964,479 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr)
}
/*
+ * Intel IGD support
+ *
+ * We need to do a few things to support Intel Integrated Graphics Devices:
+ * 1) Define a stolen memory region and trap I/O port writes programming it
+ * 2) Expose the OpRegion if one is provided to us
+ * 3) Copy key PCI config space register values from the host bridge
+ * 4) Create an LPC/ISA bridge and do the same for it.
+ *
+ * We mostly try to hide IGD stolen memory (GGMS/GMS) from the VM, but if a ROM
+ * is exposed it will try to use at least 1MB of GGMS stolen memory, apparently
+ * for VESA modes. The ROM itself seems to contain the address of the host
+ * stolen memory range as execution of the vBIOS writes these addresses without
+ * probing the hardware. Fortunately the vBIOS writes these addresses through
+ * I/O port space and they're only for use by the graphics device itself.
+ * Therefore we can intercept them without a performance penalty to native
+ * drivers and we can modify them to a new range without affecting
+ * functionality. We ask the VM BIOS to allocate a new reserved range for this
+ * with the "etc/igd-bdsm" fw_cfg file, which is not actually readable, just a
+ * convenient way of providing a named tag with size. If VGA is not disabled
+ * on IGD, we'll also automatically enable it through this process since
+ * execution of the vBIOS sort of implies VGA. This can all be disabled by
+ * passing rombar=0 to the device.
+ *
+ * The remaining quirks are all enabled through vfio device specific regions
+ * and are triggered through discovery of those regions. Exposing the OpRegion
+ * is mainly useful for the Video BIOS Table (VBT). We create a copy of the
+ * OpRegion data and ask the VM BIOS to create storage space for it and copy it
+ * into VM memory using the "etc/igd-opregion" fw_cfg file.
+ *
+ * The host and ISA bridge features are necessary for IGD versions that do not
+ * support Intel's Universal Passthrough Mode (UPT). UPT should be supported
+ * on BroadWell and newer GPUs. However, not all guest drivers support this.
+ * Therefore if the IGD is an 8th generation or newer, we'll only initialize
+ * these devices if VGA mode is not supported. Since VGA mode can be
+ * automatically enabled for the stolen memory support above, this means
+ * specifically disabling ROM support.
+ */
+
+/*
+ * This presumes the device is already known to be an Intel VGA device, so we
+ * take liberties in which device ID bits match which generation.
+ * See linux:include/drm/i915_pciids.h for IDs.
+ */
+static int igd_gen(VFIOPCIDevice *vdev)
+{
+ if ((vdev->device_id & 0xfff) == 0xa84) {
+ return 8; /* Broxton */
+ }
+
+ switch (vdev->device_id & 0xff00) {
+ /* Old, untested, unavailable, unknown */
+ case 0x0000:
+ case 0x2500:
+ case 0x2700:
+ case 0x2900:
+ case 0x2a00:
+ case 0x2e00:
+ case 0x3500:
+ case 0xa000:
+ return -1;
+ /* SandyBridge, IvyBridge, ValleyView, Haswell */
+ case 0x0100:
+ case 0x0400:
+ case 0x0a00:
+ case 0x0c00:
+ case 0x0d00:
+ case 0x0f00:
+ return 6;
+ /* BroadWell, CherryView, SkyLake, KabyLake */
+ case 0x1600:
+ case 0x1900:
+ case 0x2200:
+ case 0x5900:
+ return 8;
+ }
+
+ return 8; /* Assume newer is compatible */
+}
+
+typedef struct VFIOIGDQuirk {
+ struct VFIOPCIDevice *vdev;
+ uint32_t index;
+} VFIOIGDQuirk;
+
+#define IGD_GMCH 0x50 /* Graphics Control Register */
+#define IGD_BDSM 0x5c /* Base Data of Stolen Memory */
+#define IGD_ASLS 0xfc /* ASL Storage Register */
+
+static uint64_t vfio_igd_quirk_data_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOIGDQuirk *igd = opaque;
+ VFIOPCIDevice *vdev = igd->vdev;
+
+ igd->index = ~0;
+
+ return vfio_region_read(&vdev->bars[4].region, addr + 4, size);
+}
+
+static void vfio_igd_quirk_data_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOIGDQuirk *igd = opaque;
+ VFIOPCIDevice *vdev = igd->vdev;
+
+ /*
+ * Programming the GGMS starts at index 0x1 and uses every 4th index up
+ * through 0x3fd (ie. 0x1, 0x5, 0x9, ..., 0x3f9, 0x3fd). The address
+ * written at each index is incremented by 4k. This only accounts for 1MB,
+ * while the GGMS is potentially up to 2MB, the vBIOS doesn't seem to go
+ * beyond this and we don't have a spec reference to know if it goes up
+ * through 0x7fd.
+ */
+ if (igd->index < 0x400 && (igd->index % 4 == 1)) {
+ uint32_t base;
+
+ base = pci_get_long(vdev->pdev.config + IGD_BDSM);
+ if (!base) {
+ hw_error("vfio-igd: Guest attempted to program IGD GTT before BIOS reserved stolen memory. Unsupported BIOS?");
+ }
+
+ base |= (data & ((1 << 20) - 1));
+
+ trace_vfio_pci_igd_bar4_write(vdev->vbasedev.name,
+ igd->index, data, base);
+ data = base;
+ }
+
+ vfio_region_write(&vdev->bars[4].region, addr + 4, data, size);
+
+ /*
+ * Observation: On IVB system the vBIOS writes up through index 0x3f9,
+ * which correlates to offset 0xfe000 within the stolen memory range. That
+ * suspiciously leaves exactly one 4k page of the first 1MB unwritten and
+ * generates DMAR faults at offset 0xff000 from the host BDSM. If we do
+ * one more step, triggered on the write to index 0x3f9 to write the index
+ * and data for that last page, the issue appears resolved. Note that the
+ * GGMS may be 1MB or 2MB, but the vBIOS seems to program 1MB regardless
+ * and inspection shows the following index registers in the sequence
+ * already addresses within the first 1MB programmed.
+ */
+ if (igd->index == 0x3f9) {
+ vfio_region_write(&vdev->bars[4].region, addr, igd->index + 4, 4);
+ vfio_region_write(&vdev->bars[4].region, addr + 4, data + 0x1000, size);
+ }
+
+ igd->index = ~0;
+}
+
+static const MemoryRegionOps vfio_igd_data_quirk = {
+ .read = vfio_igd_quirk_data_read,
+ .write = vfio_igd_quirk_data_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t vfio_igd_quirk_index_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOIGDQuirk *igd = opaque;
+ VFIOPCIDevice *vdev = igd->vdev;
+
+ igd->index = ~0;
+
+ return vfio_region_read(&vdev->bars[4].region, addr, size);
+}
+
+static void vfio_igd_quirk_index_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOIGDQuirk *igd = opaque;
+ VFIOPCIDevice *vdev = igd->vdev;
+
+ igd->index = data;
+
+ vfio_region_write(&vdev->bars[4].region, addr, data, size);
+}
+
+static const MemoryRegionOps vfio_igd_index_quirk = {
+ .read = vfio_igd_quirk_index_read,
+ .write = vfio_igd_quirk_index_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(vdev);
+ struct vfio_region_info *reg_info;
+ VFIOQuirk *quirk;
+ VFIOIGDQuirk *igd;
+ int rom_size, ggms_mb, gms_mb = 0;
+ uint32_t gmch, gms_mask, gms_shift, ggms_mask, ggms_shift;
+
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) ||
+ !vfio_is_vga(vdev) || nr != 4) {
+ return;
+ }
+
+ if (vfio_get_region_info(&vdev->vbasedev,
+ VFIO_PCI_ROM_REGION_INDEX, ®_info)) {
+ error_report("Failed to get IGD ROM info");
+ return;
+ }
+
+ rom_size = reg_info->size;
+ g_free(reg_info);
+
+ if (!rom_size) {
+ return; /* No ROM, no VBIOS, no need */
+ }
+
+ /* See linux:include/drm/i915_drm.h */
+ switch (igd_gen(vdev)) {
+ case 6:
+ gms_mask = 0x1f;
+ gms_shift = 3;
+ ggms_mask = 0x3;
+ ggms_shift = 8;
+ break;
+ case 8:
+ gms_mask = 0xff;
+ gms_shift = 8;
+ ggms_mask = 0x3;
+ ggms_shift = 6;
+ break;
+ default:
+ error_report("Unknown IGD version, try SandyBridge or newer.");
+ return;
+ }
+
+ if (vdev->pdev.qdev.hotplugged) {
+ error_report("IGD should not be hot-added, good luck");
+ return;
+ }
+
+ gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4);
+ ggms_mb = (gmch >> ggms_shift) & ggms_mask; /* Read GGMS */
+ gmch &= ~(gms_mask << gms_shift); /* Mask out GMS */
+
+ if (!ggms_mb) {
+ ggms_mb = 1; /* vBIOS seems to use 1MB without checking hardware */
+ gmch |= ggms_mb << ggms_shift;
+ }
+
+ if (!(gmch & 0x2) && !vdev->vga && !vdev->no_auto_vga) {
+ if (vfio_populate_vga(vdev)) {
+ error_report("IGD VGA auto-enable failed");
+ }
+ }
+
+ dc->hotpluggable = false;
+
+ quirk = g_malloc0(sizeof(*quirk));
+ quirk->mem = g_new0(MemoryRegion, 2);
+ quirk->nr_mem = 2;
+ igd = quirk->data = g_malloc0(sizeof(*igd));
+ igd->vdev = vdev;
+ igd->index = ~0;
+
+ memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_igd_index_quirk,
+ igd, "vfio-igd-index-quirk", 4);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ 0, &quirk->mem[0], 1);
+
+ memory_region_init_io(&quirk->mem[1], OBJECT(vdev), &vfio_igd_data_quirk,
+ igd, "vfio-igd-data-quirk", 4);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ 4, &quirk->mem[1], 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+
+ /*
+ * Not sure if this is worthwhile, but we can ask the BIOS to reserve GMS
+ * stolen memory as well and report it out through the emulated GMCH
+ * register. It's only an experimental option, so it can be dropped.
+ */
+ if (vdev->igd_gms) {
+ if (vdev->igd_gms <= 0x10) {
+ gms_mb = vdev->igd_gms * 32;
+ gmch |= vdev->igd_gms << gms_shift;
+ } else {
+ error_report("Unsupported IGD GMS value 0x%x", vdev->igd_gms);
+ vdev->igd_gms = 0;
+ }
+ }
+
+ fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm",
+ NULL, (ggms_mb + gms_mb) * 1024 * 1024);
+
+ /* GMCH is read-only, emulated */
+ pci_set_long(vdev->pdev.config + IGD_GMCH, gmch);
+ pci_set_long(vdev->pdev.wmask + IGD_BDSM, 0);
+ pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0);
+
+ /* BDSM is read-write, emulated. The BIOS needs to be able to write it */
+ pci_set_long(vdev->pdev.config + IGD_BDSM, 0);
+ pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0);
+ pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0);
+
+ trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, ggms_mb + gms_mb);
+}
+
+
+int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *region)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(vdev);
+ int ret;
+
+ if (vdev->pdev.qdev.hotplugged) {
+ return -EINVAL;
+ }
+
+ dc->hotpluggable = false;
+
+ vdev->igd_opregion = g_malloc0(region->size);
+ ret = pread(vdev->vbasedev.fd, vdev->igd_opregion,
+ region->size, region->offset);
+ if (ret != region->size) {
+ error_report("vfio: Error reading IGD OpRegion");
+ g_free(vdev->igd_opregion);
+ vdev->igd_opregion = NULL;
+ return -EINVAL;
+ }
+
+ fw_cfg_add_file(fw_cfg_find(), "etc/igd-opregion",
+ vdev->igd_opregion, region->size);
+
+ trace_vfio_pci_igd_opregion_enabled(vdev->vbasedev.name);
+
+ /* Like BDSM, the BIOS writes the location of the reserved memory here */
+ pci_set_long(vdev->pdev.config + IGD_ASLS, 0);
+ pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0);
+ pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0);
+
+ return 0;
+}
+
+/* Register sets for host and LPC/ISA bridge to copy into VM */
+typedef struct {
+ uint8_t offset;
+ uint8_t len;
+} IGDHostInfo;
+
+static const IGDHostInfo igd_host_bridge_infos[] = {
+ {PCI_REVISION_ID, 2},
+ {PCI_SUBSYSTEM_VENDOR_ID, 2},
+ {PCI_SUBSYSTEM_ID, 2},
+};
+
+static const IGDHostInfo igd_lpc_bridge_infos[] = {
+ {PCI_VENDOR_ID, 2},
+ {PCI_DEVICE_ID, 2},
+ {PCI_REVISION_ID, 2},
+ {PCI_SUBSYSTEM_VENDOR_ID, 2},
+ {PCI_SUBSYSTEM_ID, 2},
+};
+
+static int vfio_pci_igd_copy(VFIOPCIDevice *vdev, PCIDevice *pdev,
+ struct vfio_region_info *region,
+ const IGDHostInfo *list, int len)
+{
+ int i, ret;
+
+ for (i = 0; i < len; i++) {
+ ret = pread(vdev->vbasedev.fd, pdev->config + list[i].offset,
+ list[i].len, region->offset + list[i].offset);
+ if (ret != list[i].len) {
+ error_report("IGD copy failed: %m");
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+int vfio_pci_igd_host_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *region)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(vdev);
+ PCIBus *bus;
+ PCIDevice *host_bridge;
+ int ret;
+
+ if (igd_gen(vdev) >= 8 && !vdev->vga) {
+ return 0;
+ }
+
+ if (vdev->pdev.qdev.hotplugged) {
+ return -EINVAL;
+ }
+
+ dc->hotpluggable = false;
+
+ bus = pci_device_root_bus(&vdev->pdev);
+ host_bridge = pci_find_device(bus, 0, PCI_DEVFN(0, 0));
+
+ if (!host_bridge) {
+ error_report("Can't find host bridge");
+ return -ENODEV;
+ }
+
+ ret = vfio_pci_igd_copy(vdev, host_bridge, region, igd_host_bridge_infos,
+ ARRAY_SIZE(igd_host_bridge_infos));
+ if (!ret) {
+ trace_vfio_pci_igd_host_bridge_enabled(vdev->vbasedev.name);
+ }
+
+ return ret;
+}
+
+static void vfio_pci_igd_lpc_bridge_realize(PCIDevice *pdev, Error **errp)
+{
+ if (pdev->devfn != PCI_DEVFN(0x1f, 0)) {
+ error_setg(errp, "VFIO dummy ISA/LPC bridge must have address 1f.0");
+ return;
+ }
+}
+
+static void vfio_pci_igd_lpc_bridge_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ dc->desc = "VFIO dummy ISA/LPC bridge for IGD assignment";
+ dc->hotpluggable = false;
+ k->realize = vfio_pci_igd_lpc_bridge_realize;
+ k->class_id = PCI_CLASS_BRIDGE_ISA;
+}
+
+static TypeInfo vfio_pci_igd_lpc_bridge_info = {
+ .name = "vfio-pci-igd-lpc-bridge",
+ .parent = TYPE_PCI_DEVICE,
+ .class_init = vfio_pci_igd_lpc_bridge_class_init,
+};
+
+static void vfio_pci_igd_register_types(void)
+{
+ type_register_static(&vfio_pci_igd_lpc_bridge_info);
+}
+
+type_init(vfio_pci_igd_register_types)
+
+int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *region)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(vdev);
+ PCIDevice *lpc_bridge;
+ int ret;
+
+ if (igd_gen(vdev) >= 8 && !vdev->vga) {
+ return 0;
+ }
+
+ if (vdev->pdev.qdev.hotplugged) {
+ return -EINVAL;
+ }
+
+ dc->hotpluggable = false;
+
+ lpc_bridge = pci_create_simple(pci_device_root_bus(&vdev->pdev),
+ PCI_DEVFN(0x1f, 0),
+ "vfio-pci-igd-lpc-bridge");
+
+ ret = vfio_pci_igd_copy(vdev, lpc_bridge, region, igd_lpc_bridge_infos,
+ ARRAY_SIZE(igd_lpc_bridge_infos));
+ if (!ret) {
+ trace_vfio_pci_igd_lpc_bridge_enabled(vdev->vbasedev.name);
+ }
+
+ return ret;
+}
+
+/*
* Common quirk probe entry points.
*/
void vfio_vga_quirk_setup(VFIOPCIDevice *vdev)
@@ -1010,6 +1485,7 @@ void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr)
vfio_probe_nvidia_bar5_quirk(vdev, nr);
vfio_probe_nvidia_bar0_quirk(vdev, nr);
vfio_probe_rtl8168_bar2_quirk(vdev, nr);
+ vfio_probe_igd_bar4_quirk(vdev, nr);
}
void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr)
@@ -2099,6 +2099,66 @@ int vfio_populate_vga(VFIOPCIDevice *vdev)
return 0;
}
+static int vfio_populate_dev_regions(VFIOPCIDevice *vdev)
+{
+ VFIODevice *vbasedev = &vdev->vbasedev;
+ struct vfio_region_info *reg_info;
+ int i, ret;
+
+ if (vbasedev->num_regions > VFIO_PCI_NUM_REGIONS) {
+ for (i = VFIO_PCI_NUM_REGIONS; i < vbasedev->num_regions; i++) {
+ struct vfio_info_cap_header *hdr;
+ struct vfio_region_info_cap_type *type;
+
+ ret = vfio_get_region_info(vbasedev, i, ®_info);
+ if (ret) {
+ continue;
+ }
+
+ hdr = vfio_get_region_info_cap(reg_info, VFIO_REGION_INFO_CAP_TYPE);
+ if (!hdr) {
+ g_free(reg_info);
+ continue;
+ }
+
+ type = container_of(hdr, struct vfio_region_info_cap_type, header);
+
+ trace_vfio_populate_dev_region(vdev->vbasedev.name, i,
+ type->type, type->subtype);
+
+ if (type->type ==
+ (VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL) &&
+ type->subtype == VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION) {
+
+ ret = vfio_pci_igd_opregion_init(vdev, reg_info);
+ if (ret) {
+ return ret;
+ }
+ } else if (type->type ==
+ (VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL) &&
+ type->subtype == VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG) {
+
+ ret = vfio_pci_igd_host_init(vdev, reg_info);
+ if (ret) {
+ return ret;
+ }
+ } else if (type->type ==
+ (VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL) &&
+ type->subtype == VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG) {
+
+ ret = vfio_pci_igd_lpc_init(vdev, reg_info);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ g_free(reg_info);
+ }
+ }
+
+ return 0;
+}
+
static int vfio_populate_device(VFIOPCIDevice *vdev)
{
VFIODevice *vbasedev = &vdev->vbasedev;
@@ -2579,6 +2639,11 @@ static int vfio_initfn(PCIDevice *pdev)
}
}
+ ret = vfio_populate_dev_regions(vdev);
+ if (ret) {
+ goto out_teardown;
+ }
+
vfio_register_err_notifier(vdev);
vfio_register_req_notifier(vdev);
vfio_setup_resetfn_quirk(vdev);
@@ -2601,6 +2666,7 @@ static void vfio_instance_finalize(Object *obj)
vfio_bars_finalize(vdev);
g_free(vdev->emulated_config_bits);
g_free(vdev->rom);
+ g_free(vdev->igd_opregion);
vfio_put_device(vdev);
vfio_put_group(group);
}
@@ -2679,12 +2745,14 @@ static Property vfio_pci_dev_properties[] = {
DEFINE_PROP_BOOL("x-no-kvm-intx", VFIOPCIDevice, no_kvm_intx, false),
DEFINE_PROP_BOOL("x-no-kvm-msi", VFIOPCIDevice, no_kvm_msi, false),
DEFINE_PROP_BOOL("x-no-kvm-msix", VFIOPCIDevice, no_kvm_msix, false),
+ DEFINE_PROP_BOOL("x-no-auto-vga", VFIOPCIDevice, no_auto_vga, false),
DEFINE_PROP_UINT32("x-pci-vendor-id", VFIOPCIDevice, vendor_id, PCI_ANY_ID),
DEFINE_PROP_UINT32("x-pci-device-id", VFIOPCIDevice, device_id, PCI_ANY_ID),
DEFINE_PROP_UINT32("x-pci-sub-vendor-id", VFIOPCIDevice,
sub_vendor_id, PCI_ANY_ID),
DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice,
sub_device_id, PCI_ANY_ID),
+ DEFINE_PROP_UINT32("x-igd-gms", VFIOPCIDevice, igd_gms, 0),
/*
* TODO - support passed fds... is this necessary?
* DEFINE_PROP_STRING("vfiofd", VFIOPCIDevice, vfiofd_name),
@@ -115,6 +115,7 @@ typedef struct VFIOPCIDevice {
int interrupt; /* Current interrupt type */
VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */
VFIOVGA *vga; /* 0xa0000, 0x3b0, 0x3c0 */
+ void *igd_opregion;
PCIHostDeviceAddress host;
EventNotifier err_notifier;
EventNotifier req_notifier;
@@ -129,6 +130,7 @@ typedef struct VFIOPCIDevice {
#define VFIO_FEATURE_ENABLE_REQ_BIT 1
#define VFIO_FEATURE_ENABLE_REQ (1 << VFIO_FEATURE_ENABLE_REQ_BIT)
int32_t bootindex;
+ uint32_t igd_gms;
uint8_t pm_cap;
bool has_vga;
bool pci_aer;
@@ -139,6 +141,7 @@ typedef struct VFIOPCIDevice {
bool no_kvm_intx;
bool no_kvm_msi;
bool no_kvm_msix;
+ bool no_auto_vga;
} VFIOPCIDevice;
uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len);
@@ -159,4 +162,10 @@ void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev);
int vfio_populate_vga(VFIOPCIDevice *vdev);
+int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *region);
+int vfio_pci_igd_host_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *region);
+int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *region);
#endif /* HW_VFIO_VFIO_PCI_H */
@@ -146,6 +146,8 @@ int vfio_get_device(VFIOGroup *group, const char *name,
VFIODevice *vbasedev);
int vfio_get_region_info(VFIODevice *vbasedev, int index,
struct vfio_region_info **info);
+struct vfio_info_cap_header *
+ vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id);
extern const MemoryRegionOps vfio_region_ops;
extern QLIST_HEAD(vfio_group_head, VFIOGroup) vfio_group_list;
@@ -1677,6 +1677,7 @@ vfio_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int
vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %s"
vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s config:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx"
vfio_populate_device_get_irq_info_failure(void) "VFIO_DEVICE_GET_IRQ_INFO failure: %m"
+vfio_populate_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%0x8"
vfio_initfn(const char *name, int group_id) " (%s) group %d"
vfio_pci_reset(const char *name) " (%s)"
vfio_pci_reset_flr(const char *name) "%s FLR/VFIO_DEVICE_RESET"
@@ -1715,7 +1716,11 @@ vfio_quirk_ati_bonaire_reset_no_smc(const char *name) "%s"
vfio_quirk_ati_bonaire_reset_timeout(const char *name) "%s"
vfio_quirk_ati_bonaire_reset_done(const char *name) "%s"
vfio_quirk_ati_bonaire_reset(const char *name) "%s"
-
+vfio_pci_igd_bar4_write(const char *name, uint32_t index, uint32_t data, uint32_t base) "%s [%03x] %08x -> %08x"
+vfio_pci_igd_bdsm_enabled(const char *name, int size) "%s %dMB"
+vfio_pci_igd_opregion_enabled(const char *name) "%s"
+vfio_pci_igd_host_bridge_enabled(const char *name) "%s"
+vfio_pci_igd_lpc_bridge_enabled(const char *name) "%s"
# hw/vfio/vfio-common.c
vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)"