From patchwork Tue Feb 16 21:32:43 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alex Williamson X-Patchwork-Id: 8332531 Return-Path: X-Original-To: patchwork-qemu-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 3E7BFC02AA for ; Tue, 16 Feb 2016 21:37:51 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A2C8D202E9 for ; Tue, 16 Feb 2016 21:37:49 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id C6DD6202E6 for ; Tue, 16 Feb 2016 21:37:47 +0000 (UTC) Received: from localhost ([::1]:50970 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aVnJa-0001WT-Ta for patchwork-qemu-devel@patchwork.kernel.org; Tue, 16 Feb 2016 16:37:46 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40768) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aVnEl-0001HY-Id for qemu-devel@nongnu.org; Tue, 16 Feb 2016 16:32:50 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aVnEj-0007gD-0q for qemu-devel@nongnu.org; Tue, 16 Feb 2016 16:32:47 -0500 Received: from mx1.redhat.com ([209.132.183.28]:58526) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aVnEi-0007fP-Cz for qemu-devel@nongnu.org; Tue, 16 Feb 2016 16:32:44 -0500 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (Postfix) with ESMTPS id 0F6E6550D5; Tue, 16 Feb 2016 21:32:44 +0000 (UTC) Received: from gimli.home (ovpn-113-102.phx2.redhat.com [10.3.113.102]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u1GLWhpF016900; Tue, 16 Feb 2016 16:32:43 -0500 From: Alex Williamson To: qemu-devel@nongnu.org Date: Tue, 16 Feb 2016 14:32:43 -0700 Message-ID: <20160216213243.13529.76786.stgit@gimli.home> In-Reply-To: <20160216211249.13529.7276.stgit@gimli.home> References: <20160216211249.13529.7276.stgit@gimli.home> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: alex.williamson@redhat.com, allen.m.kay@intel.com, kvm@vger.kernel.org Subject: [Qemu-devel] [RFC PATCH v3 9/9] Intel IGD support for vfio X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP --- hw/vfio/common.c | 2 hw/vfio/pci-quirks.c | 476 +++++++++++++++++++++++++++++++++++++++++ hw/vfio/pci.c | 68 ++++++ hw/vfio/pci.h | 9 + include/hw/vfio/vfio-common.h | 2 trace-events | 7 + 6 files changed, 562 insertions(+), 2 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 879a657..c201bee 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -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; diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 49ecf11..5828362 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -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) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 368f40d..a288fae 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -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), diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 3976f68..850de17 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -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 */ diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 594905a..45ba6a3 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.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; diff --git a/trace-events b/trace-events index c2f48af..06af953 100644 --- a/trace-events +++ b/trace-events @@ -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)"