From patchwork Sat Feb 13 00:17:24 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alex Williamson X-Patchwork-Id: 582338 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 2B197140B0E for ; Sat, 13 Feb 2016 11:22:27 +1100 (AEDT) Received: from localhost ([::1]:37553 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aUNyj-0004wK-8B for incoming@patchwork.ozlabs.org; Fri, 12 Feb 2016 19:22:25 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:57431) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aUNtz-0005kT-Nf for qemu-devel@nongnu.org; Fri, 12 Feb 2016 19:17:33 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aUNtw-0005eM-FE for qemu-devel@nongnu.org; Fri, 12 Feb 2016 19:17:31 -0500 Received: from mx1.redhat.com ([209.132.183.28]:55645) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aUNtt-0005cs-CX for qemu-devel@nongnu.org; Fri, 12 Feb 2016 19:17:28 -0500 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (Postfix) with ESMTPS id 134BFC0AED42; Sat, 13 Feb 2016 00:17:25 +0000 (UTC) Received: from gimli.home (ovpn-113-102.phx2.redhat.com [10.3.113.102]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u1D0HOUM011195; Fri, 12 Feb 2016 19:17:24 -0500 From: Alex Williamson To: qemu-devel@nongnu.org Date: Fri, 12 Feb 2016 17:17:24 -0700 Message-ID: <20160213001724.17724.62165.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.26 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 9/9] vfio/pci: Intel IGD stolen memory quirk 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 The IGD vBIOS really wants to use stolen memory, which it programs into the device using I/O port BAR4. This is a typical dword index and data register where indexes below 0x400 seem to be writing stolen memory addresses. The vBIOS apparently comes up with the address of stolen memory itself or it's written into the ROM, because it's the host stolen memory location. Since we really don't want to identity map the host stolen memory into the guest, we intercept these writes and instead program the device with memory in the VM, reserved for us by SeaBIOS. The result is that the vBIOS works as intended and we don't get any DMAR faults or VM memory corruption by the graphics device trying to use host memory addresses. Of course being able to do this implies that the stolen memory is fire-and-forget for the vBIOS, ie. it doesn't actually access the memory range itself. This does seem to be the case since it works. Signed-off-by: Alex Williamson --- hw/vfio/pci-quirks.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index f6e83b0..103aa2a 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -964,6 +964,140 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr) } /* + * Intel IGD graphics makes use of stolen memory. We'd really like to ignore + * it, guest drives don't use it, but lighting up a laptop panel seems to + * require support in the vBIOS and the vBIOS does use stolen memory. Even + * more interesting, the vBIOS writes the host stolen memory addresses to the + * device, which must be stored in the ROM. To handle this, we setup a quirk + * on the I/O port BAR, which is where the vBIOS performs this programming. + * The first two dwords of the BAR are and index and data register. Indexes + * less than 0x400 select a data register for setting up this stolen memory + * area. The region is minimally 1MB aligned, so we keep the offset and + * replace it with the address of the BDSM found on the device. This register + * has hopefully been programmed by SeaBIOS with the address of a 1MB buffer + * in reserved memory. + */ +typedef struct VFIOIGDQuirk { + struct VFIOPCIDevice *vdev; + uint32_t index; +} VFIOIGDQuirk; + + +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; + + if (igd->index < 0x400) { + uint32_t bdsm; + + pread(vdev->vbasedev.fd, &bdsm, 4, vdev->config_offset + 0x5c); + bdsm &= ~((1 << 20) - 1); + if (bdsm) { + data &= (1 << 20) - 1; + data |= bdsm; + } else { + error_report("Guest wrote IGD stolen memory and we have nowhere to redirect to - update SeaBIOS?"); + } + } + + 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 1MB stolen memory range. + * This leaves the last index at 0xff000 unprogrammed resulting in DMAR + * faults to offset 0xff000 from the host BDSM address. If we do one + * more step to program that last index, these go away. Maybe this is + * a latent vBIOS bug that doesn't occur when nobody is frobbing the + * stolen memory address? The index register starts at 0x1 and is + * incremented by 4, the data register starts at 0 and increments by 4k. + */ + 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) +{ + VFIOQuirk *quirk; + VFIOIGDQuirk *igd; + + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || + !vfio_is_vga(vdev) || nr != 4) { + return; + } + + 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); +} + +/* * Common quirk probe entry points. */ void vfio_vga_quirk_setup(VFIOPCIDevice *vdev) @@ -1012,6 +1146,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)