* [Qemu-devel] [RFC PATCH v5 6/7] vfio/pci: Intel graphics legacy mode assignment
2016-04-21 15:56 [Qemu-devel] [RFC PATCH v5 0/7] vfio IGD assignment Alex Williamson
` (4 preceding siblings ...)
2016-04-21 15:56 ` [Qemu-devel] [RFC PATCH v5 5/7] vfio/pci: Setup BAR quirks after capabilities probing Alex Williamson
@ 2016-04-21 15:56 ` Alex Williamson
2016-04-21 15:56 ` [Qemu-devel] [RFC PATCH v5 7/7] vfio/pci: Add a separate option for IGD OpRegion support Alex Williamson
2016-04-25 9:59 ` [Qemu-devel] [RFC PATCH v5 0/7] vfio IGD assignment nicolas prochazka
7 siblings, 0 replies; 10+ messages in thread
From: Alex Williamson @ 2016-04-21 15:56 UTC (permalink / raw)
To: qemu-devel; +Cc: allen.m.kay, kraxel, kvm
Enable quirks to support SandyBridge and newer IGD devices as primary
VM graphics. This requires new vfio-pci device specific regions added
in kernel v4.6 to expose the IGD OpRegion, the shadow ROM, and config
space access to the PCI host bridge and LPC/ISA bridge. VM firmware
support, SeaBIOS only so far, is also required for reserving memory
regions for IGD specific use. In order to enable this mode, IGD must
be assigned to the VM at PCI bus address 00:02.0, it must have a ROM,
it must be able to enable VGA, it must have or be able to create on
its own an LPC/ISA bridge of the proper type at PCI bus address
00:1f.0 (sorry, not compatible with Q35 yet), and it must have the
above noted vfio-pci kernel features and BIOS. The intention is that
to enable this mode, a user simply needs to assign 00:02.0 from the
host to 00:02.0 in the VM:
-device vfio-pci,host=0000:00:02.0,bus=pci.0,addr=02.0
and everything either happens automatically or it doesn't. In the
case that it doesn't, we leave error reports, but assume the device
will operate in universal passthrough mode (UPT), which doesn't
require any of this, but has a much more narrow window of supported
devices, supported use cases, and supported guest drivers.
When using IGD in this mode, the VM firmware is required to reserve
some VM RAM for the OpRegion (on the order or several 4k pages) and
stolen memory for the GTT (up to 8MB for the latest GPUs). An
additional option, x-igd-gms allows the user to specify some amount
of additional memory (value is number of 32MB chunks up to 512MB) that
is pre-allocated for graphics use. TBH, I don't know of anything that
requires this or makes use of this memory, which is why we don't
allocate any by default, but the specification suggests this is not
actually a valid combination, so the option exists as a workaround.
Please report if it's actually necessary in some environment.
See code comments for further discussion about the actual operation
of the quirks necessary to assign these devices.
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
hw/vfio/pci-quirks.c | 617 ++++++++++++++++++++++++++++++++++++++++++++++++++
hw/vfio/pci.c | 2
hw/vfio/pci.h | 2
trace-events | 5
4 files changed, 625 insertions(+), 1 deletion(-)
diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c
index 49ecf11..6feadb9 100644
--- a/hw/vfio/pci-quirks.c
+++ b/hw/vfio/pci-quirks.c
@@ -11,9 +11,12 @@
*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/range.h"
+#include "qapi/error.h"
+#include "hw/nvram/fw_cfg.h"
#include "pci.h"
#include "trace.h"
-#include "qemu/range.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 +965,617 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr)
}
/*
+ * Intel IGD support
+ *
+ * Obviously IGD is not a discrete device, this is evidenced not only by it
+ * being integrated into the CPU, but by the various chipset and BIOS
+ * dependencies that it brings along with it. Intel is trying to move away
+ * from this and Broadwell and newer devices can run in what Intel calls
+ * "Universal Pass-Through" mode, or UPT. Theoretically in UPT mode, nothing
+ * more is required beyond assigning the IGD device to a VM. There are
+ * however support limitations to this mode. It only supports IGD as a
+ * secondary graphics device in the VM and it doesn't officially support any
+ * physical outputs.
+ *
+ * The code here attempts to enable what we'll call legacy mode assignment,
+ * IGD retains most of the capabilities we expect for it to have on bare
+ * metal. To enable this mode, the IGD device must be assigned to the VM
+ * at PCI address 00:02.0, it must have a ROM, it very likely needs VGA
+ * support, we must have VM BIOS support for reserving and populating some
+ * of the required tables, and we need to tweak the chipset with revisions
+ * and IDs and an LPC/ISA bridge device. The intention is to make all of
+ * this happen automatically by installing the device at the correct VM PCI
+ * bus address. If any of the conditions are not met, we cross our fingers
+ * and hope the user knows better.
+ *
+ * NB - It is possible to enable physical outputs in UPT mode by supplying
+ * an OpRegion table. We don't do this by default because the guest driver
+ * behaves differently if an OpRegion is provided and no monitor is attached
+ * vs no OpRegion and a monitor being attached or not. Effectively, if a
+ * headless setup is desired, the OpRegion gets in the way of that.
+ */
+
+/*
+ * 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 */
+
+/*
+ * The OpRegion includes the Video BIOS Table, which seems important for
+ * telling the driver what sort of outputs it has. Without this, the device
+ * may work in the guest, but we may not get output. This also requires BIOS
+ * support to reserve and populate a section of guest memory sufficient for
+ * the table and to write the base address of that memory to the ASLS register
+ * of the IGD device.
+ */
+static int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *info)
+{
+ int ret;
+
+ vdev->igd_opregion = g_malloc0(info->size);
+ ret = pread(vdev->vbasedev.fd, vdev->igd_opregion,
+ info->size, info->offset);
+ if (ret != info->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, info->size);
+
+ trace_vfio_pci_igd_opregion_enabled(vdev->vbasedev.name);
+
+ 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;
+}
+
+/*
+ * The rather short list of registers that we copy from the host devices.
+ * The LPC/ISA bridge values are definitely needed to support the vBIOS, the
+ * host bridge values may or may not be needed depending on the guest OS.
+ * Since we're only munging revision and subsystem values on the host bridge,
+ * we don't require our own device. The LPC/ISA bridge needs to be our very
+ * own though.
+ */
+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 *info,
+ 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, info->offset + list[i].offset);
+ if (ret != list[i].len) {
+ error_report("IGD copy failed: %m");
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Stuff a few values into the host bridge.
+ */
+static int vfio_pci_igd_host_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *info)
+{
+ PCIBus *bus;
+ PCIDevice *host_bridge;
+ int ret;
+
+ 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, info, igd_host_bridge_infos,
+ ARRAY_SIZE(igd_host_bridge_infos));
+ if (!ret) {
+ trace_vfio_pci_igd_host_bridge_enabled(vdev->vbasedev.name);
+ }
+
+ return ret;
+}
+
+/*
+ * IGD LPC/ISA bridge support code. The vBIOS needs this, but we can't write
+ * arbitrary values into just any bridge, so we must create our own. We try
+ * to handle if the user has created it for us, which they might want to do
+ * to enable multifuction so we don't occupy the whole PCI slot.
+ */
+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)
+
+static int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *info)
+{
+ PCIDevice *lpc_bridge;
+ int ret;
+
+ lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev),
+ 0, PCI_DEVFN(0x1f, 0));
+ if (!lpc_bridge) {
+ 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, info, igd_lpc_bridge_infos,
+ ARRAY_SIZE(igd_lpc_bridge_infos));
+ if (!ret) {
+ trace_vfio_pci_igd_lpc_bridge_enabled(vdev->vbasedev.name);
+ }
+
+ return ret;
+}
+
+/*
+ * IGD Gen8 and newer support up to 8MB for the GTT and use a 64bit PTE
+ * entry, older IGDs use 2MB and 32bit. Each PTE maps a 4k page. Therefore
+ * we either have 2M/4k * 4 = 2k or 8M/4k * 8 = 16k as the maximum iobar index
+ * for programming the GTT.
+ *
+ * See linux:include/drm/i915_drm.h for shift and mask values.
+ */
+static int vfio_igd_gtt_max(VFIOPCIDevice *vdev)
+{
+ uint32_t gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch));
+ int ggms, gen = igd_gen(vdev);
+
+ gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch));
+ ggms = (gmch >> (gen < 8 ? 8 : 6)) & 0x3;
+ if (gen > 6) {
+ ggms = 1 << ggms;
+ }
+
+ ggms *= 1024 * 1024;
+
+ return (ggms / (4 * 1024)) * (gen < 8 ? 4 : 8);
+}
+
+/*
+ * The IGD ROM will make use of stolen memory (GGMS) for support of VESA modes.
+ * Somehow the host stolen memory range is used for this, but how the ROM gets
+ * it is a mystery, perhaps it's hardcoded into the ROM. Thankfully though, it
+ * reprograms the GTT through the IOBAR where we can trap it and transpose the
+ * programming to the VM allocated buffer. That buffer gets reserved by the VM
+ * firmware via the fw_cfg entry added below. Here we're just monitoring the
+ * IOBAR address and data registers to detect a write sequence targeting the
+ * GTTADR. This code is developed by observed behavior and doesn't have a
+ * direct spec reference, unfortunately.
+ */
+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;
+ uint64_t val = data;
+ int gen = igd_gen(vdev);
+
+ /*
+ * Programming the GGMS starts at index 0x1 and uses every 4th index (ie.
+ * 0x1, 0x5, 0x9, 0xd,...). For pre-Gen8 each 4-byte write is a whole PTE
+ * entry, with 0th bit enable set. For Gen8 and up, PTEs are 64bit, so
+ * entries 0x5 & 0xd are the high dword, in our case zero. Each PTE points
+ * to a 4k page, which we translate to a page from the VM allocated region,
+ * pointed to by the BDSM register. If this is not set, we fail.
+ *
+ * We trap writes to the full configured GTT size, but we typically only
+ * see the vBIOS writing up to (nearly) the 1MB barrier. In fact it often
+ * seems to miss the last entry for an even 1MB GTT. Doing a gratuitous
+ * write of that last entry does work, but is hopefully unnecessary since
+ * we clear the previous GTT on initialization.
+ */
+ if (igd->index % 4 == 1 && igd->index < vfio_igd_gtt_max(vdev)) {
+ if (gen < 8 || igd->index % 8 == 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?");
+ }
+
+ val = base | (data & ((1 << 20) - 1));
+ } else {
+ val = 0; /* upper 32bits of pte, we only enable below 4G PTEs */
+ }
+
+ trace_vfio_pci_igd_bar4_write(vdev->vbasedev.name,
+ igd->index, data, val);
+ }
+
+ vfio_region_write(&vdev->bars[4].region, addr + 4, val, 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)
+{
+ struct vfio_region_info *rom, *opregion, *host, *lpc;
+ VFIOQuirk *quirk;
+ VFIOIGDQuirk *igd;
+ PCIDevice *lpc_bridge;
+ int i, ret, ggms_mb, gms_mb = 0, gen;
+ uint32_t gmch;
+ uint16_t cmd_orig, cmd;
+
+ /*
+ * This must be an Intel VGA device at address 00:02.0 for us to even
+ * consider enabling legacy mode. The vBIOS has dependencies on the
+ * PCI bus address.
+ */
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) ||
+ !vfio_is_vga(vdev) || nr != 4 ||
+ &vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev),
+ 0, PCI_DEVFN(0x2, 0))) {
+ return;
+ }
+
+ /*
+ * We need to create an LPC/ISA bridge at PCI bus address 00:1f.0 that we
+ * can stuff host values into, so if there's already one there and it's not
+ * one we can hack on, legacy mode is no-go. Sorry Q35.
+ */
+ lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev),
+ 0, PCI_DEVFN(0x1f, 0));
+ if (lpc_bridge && !object_dynamic_cast(OBJECT(lpc_bridge),
+ "vfio-pci-igd-lpc-bridge")) {
+ error_report("IGD device %s cannot support legacy mode due to existing "
+ "devices at address 1f.0", vdev->vbasedev.name);
+ return;
+ }
+
+ /*
+ * IGD is not a standard, they like to change their specs often. We
+ * only attempt to support back to SandBridge and we hope that newer
+ * devices maintain compatibility with generation 8.
+ */
+ gen = igd_gen(vdev);
+ if (gen != 6 && gen != 8) {
+ error_report("IGD device %s is unsupported in legacy mode, "
+ "try SandyBridge or newer", vdev->vbasedev.name);
+ return;
+ }
+
+ /*
+ * Most of what we're doing here is to enable the ROM to run, so if
+ * there's no ROM, there's no point in setting up this quirk.
+ */
+ ret = vfio_get_region_info(&vdev->vbasedev,
+ VFIO_PCI_ROM_REGION_INDEX, &rom);
+ if ((ret || !rom->size) && !vdev->pdev.romfile) {
+ error_report("IGD device %s has no ROM, legacy mode disabled",
+ vdev->vbasedev.name);
+ goto out;
+ }
+
+ /*
+ * Ignore the hotplug corner case, mark the ROM failed, we can't
+ * create the devices we need for legacy mode in the hotplug scenario.
+ */
+ if (vdev->pdev.qdev.hotplugged) {
+ error_report("IGD device %s hotplugged, ROM disabled, "
+ "legacy mode disabled", vdev->vbasedev.name);
+ vdev->rom_read_failed = true;
+ goto out;
+ }
+
+ /*
+ * Check whether we have all the vfio device specific regions to
+ * support legacy mode. If not, bail.
+ */
+ ret = vfio_get_dev_region_info(&vdev->vbasedev,
+ VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL,
+ VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion);
+ if (ret) {
+ error_report("IGD device %s does not support OpRegion access,"
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ ret = vfio_get_dev_region_info(&vdev->vbasedev,
+ VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL,
+ VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG, &host);
+ if (ret) {
+ error_report("IGD device %s does not support host bridge access,"
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ ret = vfio_get_dev_region_info(&vdev->vbasedev,
+ VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL,
+ VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG, &lpc);
+ if (ret) {
+ error_report("IGD device %s does not support LPC bridge access,"
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4);
+
+ /*
+ * If IGD VGA Disable is clear (expected) and VGA is not already enabled,
+ * try to enable it. Probably shouldn't be using legacy mode without VGA,
+ * but also no point in us enabling VGA if disabled in hardware.
+ */
+ if (!(gmch & 0x2) && !vdev->vga && vfio_populate_vga(vdev)) {
+ error_report("IGD device %s failed to enable VGA access, "
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ /* Create our LPC/ISA bridge */
+ ret = vfio_pci_igd_lpc_init(vdev, lpc);
+ if (ret) {
+ error_report("IGD device %s failed to create LPC bridge, "
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ /* Stuff some host values into the VM PCI host bridge */
+ ret = vfio_pci_igd_host_init(vdev, host);
+ if (ret) {
+ error_report("IGD device %s failed to modify host bridge, "
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ /* Setup OpRegion access */
+ ret = vfio_pci_igd_opregion_init(vdev, opregion);
+ if (ret) {
+ error_report("IGD device %s failed to setup OpRegion, "
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ /* Setup our quirk to munge GTT addresses to the VM allocated buffer */
+ 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);
+
+ /* Determine the size of stolen memory needed for GTT */
+ ggms_mb = (gmch >> (gen < 8 ? 8 : 6)) & 0x3;
+ if (gen > 6) {
+ ggms_mb = 1 << ggms_mb;
+ }
+
+ /*
+ * Assume we have no GMS memory, but allow it to be overrided by device
+ * option (experimental). The spec doesn't actually allow zero GMS when
+ * when IVD (IGD VGA Disable) is clear, but the claim is that it's unused,
+ * so let's not waste VM memory for it.
+ */
+ gmch &= ~((gen < 8 ? 0x1f : 0xff) << (gen < 8 ? 3 : 8));
+
+ if (vdev->igd_gms) {
+ if (vdev->igd_gms <= 0x10) {
+ gms_mb = vdev->igd_gms * 32;
+ gmch |= vdev->igd_gms << (gen < 8 ? 3 : 8);
+ } else {
+ error_report("Unsupported IGD GMS value 0x%x", vdev->igd_gms);
+ vdev->igd_gms = 0;
+ }
+ }
+
+ /* We convey only the size via the fw_cfg entry, data is NULL */
+ 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_GMCH, 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);
+
+ /*
+ * This IOBAR gives us access to GTTADR, which allows us to write to
+ * the GTT itself. So let's go ahead and write zero to all the GTT
+ * entries to avoid spurious DMA faults. Be sure I/O access is enabled
+ * before talking to the device.
+ */
+ if (pread(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig),
+ vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) {
+ error_report("IGD device %s - failed to read PCI command register",
+ vdev->vbasedev.name);
+ }
+
+ cmd = cmd_orig | PCI_COMMAND_IO;
+
+ if (pwrite(vdev->vbasedev.fd, &cmd, sizeof(cmd),
+ vdev->config_offset + PCI_COMMAND) != sizeof(cmd)) {
+ error_report("IGD device %s - failed to write PCI command register",
+ vdev->vbasedev.name);
+ }
+
+ for (i = 1; i < vfio_igd_gtt_max(vdev); i += 4) {
+ vfio_region_write(&vdev->bars[4].region, 0, i, 4);
+ vfio_region_write(&vdev->bars[4].region, 4, 0, 4);
+ }
+
+ if (pwrite(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig),
+ vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) {
+ error_report("IGD device %s - failed to restore PCI command register",
+ vdev->vbasedev.name);
+ }
+
+ trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, ggms_mb + gms_mb);
+
+out:
+ g_free(rom);
+ g_free(opregion);
+ g_free(host);
+ g_free(lpc);
+}
+
+/*
* Common quirk probe entry points.
*/
void vfio_vga_quirk_setup(VFIOPCIDevice *vdev)
@@ -1010,6 +1624,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 aa6fb7b..0a745f6 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -2605,6 +2605,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);
}
@@ -2689,6 +2690,7 @@ static Property vfio_pci_dev_properties[] = {
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..31ee8da 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;
diff --git a/trace-events b/trace-events
index d099a2c..fd91bd2 100644
--- a/trace-events
+++ b/trace-events
@@ -1714,6 +1714,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/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)"
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [RFC PATCH v5 0/7] vfio IGD assignment
2016-04-21 15:56 [Qemu-devel] [RFC PATCH v5 0/7] vfio IGD assignment Alex Williamson
` (6 preceding siblings ...)
2016-04-21 15:56 ` [Qemu-devel] [RFC PATCH v5 7/7] vfio/pci: Add a separate option for IGD OpRegion support Alex Williamson
@ 2016-04-25 9:59 ` nicolas prochazka
2016-04-25 14:11 ` Alex Williamson
7 siblings, 1 reply; 10+ messages in thread
From: nicolas prochazka @ 2016-04-25 9:59 UTC (permalink / raw)
To: Alex Williamson
Cc: qemu-devel, allen.m.kay, Gerd Hoffmann, kvm@vger.kernel.org
[-- Attachment #1: Type: text/plain, Size: 6708 bytes --]
hello again,
I'm compiling linux kernel 4.6rc5
i'm compiling your qemu and seabios git
then , I unbind my vga card :
echo "0000:00:02.0" > /sys/bus/pci/devices/0000\:00\:02.0/driver/unbind
and start qemu with this command line ( from ssh )
/usr/bin/qemu-kvm -name win -S -machine pc-i440fx-2.6,accel=kvm,usb=off
-cpu host,hv_time,hv_relaxed,hv_vapic,hv_spinlocks=0x1fff -m 14257
-mem-prealloc -mem-path /dev/hugepages/libvirt/qemu -realtime mlock=off
-smp 2,sockets=1,cores=2,threads=1 -uuid
ab829652-e53a-32bf-9a18-6722c7e7f785 -global PIIX4_PM.disable_s3=1 -global
PIIX4_PM.disable_s4=1 -boot strict=on -device
piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 -device
virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x4 -drive
file=nbd:unix:/tmp/win.ctl,if=none,id=drive-ide0-0-0,format=raw -device
ide-hd,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0,bootindex=1 -drive
file=nbd:unix:/tmp/swap.ctl,if=none,id=drive-ide0-0-1,format=raw -device
ide-hd,bus=ide.0,unit=1,drive=drive-ide0-0-1,id=ide0-0-1 -device
vfio-pci,host=0000:00:02.0,bus=pci.0,addr=02.0 -vga none
*qemu-system-x86_64: -device
vfio-pci,host=0000:00:02.0,bus=pci.0,addr=02.0: PCI: slot 2 function 0 not
available for vfio-pci, in use by e1000*
what is wrong ?
regards,
Nicolas
lspci shows us :
00:00.0 Host bridge [0600]: Intel Corporation Sky Lake Host Bridge/DRAM
Registers [8086:1904] (rev 09)
00:02.0 VGA compatible controller [0300]: Intel Corporation Sky Lake
Integrated Graphics [8086:1926] (rev 0a)
00:14.0 USB controller [0c03]: Intel Corporation Device [8086:9d2f] (rev 21)
00:14.2 Signal processing controller [1180]: Intel Corporation Device
[8086:9d31] (rev 21)
00:16.0 Communication controller [0780]: Intel Corporation Device
[8086:9d3a] (rev 21)
00:17.0 SATA controller [0106]: Intel Corporation Device [8086:9d03] (rev
21)
00:1c.0 PCI bridge [0604]: Intel Corporation Device [8086:9d14] (rev f1)
00:1d.0 PCI bridge [0604]: Intel Corporation Device [8086:9d18] (rev f1)
00:1e.0 Signal processing controller [1180]: Intel Corporation Device
[8086:9d27] (rev 21)
00:1e.6 SD Host controller [0805]: Intel Corporation Device [8086:9d2d]
(rev 21)
00:1f.0 ISA bridge [0601]: Intel Corporation Device [8086:9d48] (rev 21)
00:1f.2 Memory controller [0580]: Intel Corporation Device [8086:9d21] (rev
21)
00:1f.3 Audio device [0403]: Intel Corporation Device [8086:9d70] (rev 21)
00:1f.4 SMBus [0c05]: Intel Corporation Device [8086:9d23] (rev 21)
00:1f.6 Ethernet controller [0200]: Intel Corporation Ethernet Connection
I219-V [8086:1570] (rev 21)
01:00.0 Network controller [0280]: Intel Corporation Wireless 8260
[8086:24f3] (rev 3a)
02:00.0 Non-Volatile memory controller [0108]: Samsung Electronics Co Ltd
Device [144d:a802] (rev 01)
2016-04-21 17:56 GMT+02:00 Alex Williamson <alex.williamson@redhat.com>:
> This series enables "legacy mode" Intel graphics device assignment on
> SandyBridge and newer CPUs when coupled with a v4.6 Linux host kernel
> and updated SeaBIOS[1]. Legacy mode assignment in this context means
> that we modify the VM to supply the features required for IGD, such as
> an OpRegion, stolen memory, host bridge and ISA bridge, and VGA, as
> opposed to Intel's Universal Pass-Through (UPT) mode where IGD can be
> assigned as if it were a discrete PCI device, with no additional
> modifications of the VM. The benefit of legacy mode is that we can
> support older hardware, primary graphics mode in the VM, and physical
> display outputs.
>
> Additionally this series adds an new x-igd-opregion=on option which is
> intended to be coupled with UPT mode to enable physically connected
> displays. This has the same SeaBIOS requirement as legacy mode.
>
> What's new in v5? Support for generation 8 and newer IGD, such as
> Broadwell and Skylake. These GPUs support 64bit PTEs, changing the
> GTT sizing and layout. We also now invalidate the GTT which avoids
> the majority of DMAR faults when assigning these devices. OpRegion
> support is no longer automatic, except in legacy mode, since it can
> interfere with headless UPT setups.
>
> Legacy mode will only be enabled when IGD is configured at VM address
> 00:02.0, a ROM is present, VGA is available, and the host kernel
> supports vfio device specific regions providing the OpRegion, host
> config space, and ISA/LPC bridge config access.
>
> What's left to do? Obviously this is post-2.6 material, otherwise
> the remaining blocker is SeaBIOS support. SeaBIOS would prefer an
> interface that allows reserved memory regions to be assigned and
> populated generically and the base address returned to QEMU without
> SeaBIOS needing device specific code.
>
> What about OVMF/Q35? The VGA ROM is a critical component of IGD
> legacy mode, but IME the ROM is only a legacy ROM without UEFI
> support. Therefore I expect it would only be compatible with OVMF
> when run with a CSM, which is not our default. OpRegion support is
> certainly something we can investigate with OVMF for UPT+OpRegion
> mode on an OVMF VM. Q35 is unfortunately incompatible with legacy
> mode because in most configurations it already places an LPC/ISA
> bridge at address 00:1f.0 in the VM. We can't very well modify that
> PCI device to report itself as the host LPC bridge since the feature
> set of the Q35 bridge may be different. Again, UPT mode is the
> solution here. Legacy mode will not fail due to a Q35 machine type,
> but will fail if address 00:1f.0 is populated with anything other
> than a vfio-pci-igd-lpc-bridge device.
>
> Note to laptop users, I generally expect that screen dimming and lid
> switches are implemented in ACPI, outside of anything supported here.
> Laptop displays are also more fickle with generic drivers and may not
> work without native device drivers (ie. the windows basic driver may
> not drive the display during an install).
>
> Please test and review. Thanks,
>
> Alex
>
> [1] http://patchwork.ozlabs.org/patch/583731
> ---
>
> Alex Williamson (7):
> vfio: Enable sparse mmap capability
> vfio: Create device specific region info helper
> vfio/pci: Fix return of vfio_populate_vga()
> vfio/pci: Consolidate VGA setup
> vfio/pci: Setup BAR quirks after capabilities probing
> vfio/pci: Intel graphics legacy mode assignment
> vfio/pci: Add a separate option for IGD OpRegion support
>
>
> hw/vfio/common.c | 103 +++++++
> hw/vfio/pci-quirks.c | 617
> +++++++++++++++++++++++++++++++++++++++++
> hw/vfio/pci.c | 145 ++++++----
> hw/vfio/pci.h | 8 +
> include/hw/vfio/vfio-common.h | 2
> trace-events | 11 +
> 6 files changed, 824 insertions(+), 62 deletions(-)
>
>
[-- Attachment #2: Type: text/html, Size: 7726 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread