From patchwork Sat Feb 13 00:17:12 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alex Williamson X-Patchwork-Id: 582336 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id C51C5140B0E for ; Sat, 13 Feb 2016 11:21:09 +1100 (AEDT) Received: from localhost ([::1]:37530 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aUNxT-0003AV-IV for incoming@patchwork.ozlabs.org; Fri, 12 Feb 2016 19:21:07 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:57284) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aUNtl-0005PV-Fn for qemu-devel@nongnu.org; Fri, 12 Feb 2016 19:17:19 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aUNti-0005V9-7R for qemu-devel@nongnu.org; Fri, 12 Feb 2016 19:17:17 -0500 Received: from mx1.redhat.com ([209.132.183.28]:33286) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aUNth-0005Up-Tp for qemu-devel@nongnu.org; Fri, 12 Feb 2016 19:17:14 -0500 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (Postfix) with ESMTPS id 97EC77AEBF; Sat, 13 Feb 2016 00:17:13 +0000 (UTC) Received: from gimli.home (ovpn-113-102.phx2.redhat.com [10.3.113.102]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u1D0HC1B009262; Fri, 12 Feb 2016 19:17:13 -0500 From: Alex Williamson To: qemu-devel@nongnu.org Date: Fri, 12 Feb 2016 17:17:12 -0700 Message-ID: <20160213001712.17724.1735.stgit@gimli.home> In-Reply-To: <20160213000436.17724.35780.stgit@gimli.home> References: <20160213000436.17724.35780.stgit@gimli.home> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 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 v2 7/9] vfio/pci: Intel IGD graphics support 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org In order to support device assignment of Intel IGD graphics, we need support three quirks, which are enabled through vfio using device specific regions. The first quirk is to expose the OpRegion of the host. This contains a Video BIOS Table (VBT), that often provides graphics modes that are useful for laptops. We store the OpRegion data from vfio in a buffer and pass it to SeaBIOS via fw_cfg. The ASL Storage register (0xFC) is made writable so the BIOS can use this register to inform the guest OS of the allocated OpRegion in VM memory. If we were to find a use for any passthrough of the host OpRegion into the guest (ie. volatile status bits and whatnot), we could use the ASLS register write as a trigger to map the host OpRegion into the VM. The second quirk is to copy some of the revisions and identification fields from PCI config space on the host bridge into the guest. Vfio provides a separate region for providing read-only access to the host bridge config space to make this possible. The third and final quirk is to create an LPC/ISA bridge in the VM at address 00:1f.0 and also populate some of its config space with host data for revision and identification. Vfio provides yet another region for read-only access to this configuration space on the host. For Broadwell and newer graphics and new enough guest graphics drivers (at least in Windows), these latter two quirks supposedly aren't necessary, but since we don't know what driver is running in the guest, we currently enable these regardless of the graphics revision. Signed-off-by: Alex Williamson --- hw/vfio/common.c | 2 hw/vfio/pci-quirks.c | 176 +++++++++++++++++++++++++++++++++++++++++ hw/vfio/pci.c | 48 +++++++++++ hw/vfio/pci.h | 8 ++ include/hw/vfio/vfio-common.h | 2 trace-events | 4 + 6 files changed, 238 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..f6e83b0 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) @@ -1203,3 +1205,177 @@ void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev) break; } } + +/* + * Intel IGD support + * + * We need to do a few things to support Intel Integrated Graphics Devices: + * 1) Expose the OpRegion if one is provided to us + * 2) Copy key PCI config space register values from the host bridge + * 3) Create an LPC/ISA bridge and do the same for it. + * + * Each of these is supported in vfio-pci through the use of device specific + * regions. The main vfio-pci driver calls out to the init functions here + * for each of those found regions. All three of these operations are + * necessary for SandyBridge, IvyBridge, ValleyView, and Haswell graphics. + * For newer versions of IGD, such as Broadwell and SkyLake, Intel supports + * an assignment mode without requiring 2) and 3). Since we don't know what + * driver will be used in the guest, we don't do any filtering here but that + * may change at some point. + */ +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\n"); + 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); + + return 0; +} + +/* Define config register sets to copy from the host devices */ +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\n"); + 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 (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 (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; +} diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 8e20781..4c376a8 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2123,6 +2123,53 @@ static int vfio_populate_device(VFIOPCIDevice *vdev) QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].quirks); } + 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); + 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) { + goto error; + } + } 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) { + goto error; + } + } 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) { + goto error; + } + } + + g_free(reg_info); + } + } + irq_info.index = VFIO_PCI_ERR_IRQ_INDEX; ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info); @@ -2557,6 +2604,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); } diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index b8a7189..b183251 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; @@ -157,4 +158,11 @@ void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr); void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr); void vfio_setup_resetfn_quirk(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..3233dd1 100644 --- a/trace-events +++ b/trace-events @@ -1715,7 +1715,9 @@ 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_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)"