From patchwork Fri Jan 28 00:45:54 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alex Williamson X-Patchwork-Id: 513501 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p0S0k2M0004191 for ; Fri, 28 Jan 2011 00:46:03 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752754Ab1A1Ap6 (ORCPT ); Thu, 27 Jan 2011 19:45:58 -0500 Received: from mx1.redhat.com ([209.132.183.28]:1963 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752576Ab1A1Ap6 (ORCPT ); Thu, 27 Jan 2011 19:45:58 -0500 Received: from int-mx01.intmail.prod.int.phx2.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id p0S0jt1B005017 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Thu, 27 Jan 2011 19:45:55 -0500 Received: from [10.3.113.10] ([10.3.113.10]) by int-mx01.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id p0S0jseS025179; Thu, 27 Jan 2011 19:45:55 -0500 Subject: Re: Graphics pass-through From: Alex Williamson To: =?ISO-8859-1?Q?Andr=E9?= Weidemann Cc: Prasad Joshi , kvm@vger.kernel.org, Oswaldo Cadenas In-Reply-To: <4D415D70.8070105@web.de> References: <4D415D70.8070105@web.de> Date: Thu, 27 Jan 2011 17:45:54 -0700 Message-ID: <1296175554.2891.29.camel@x201> Mime-Version: 1.0 X-Scanned-By: MIMEDefang 2.67 on 10.5.11.11 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Fri, 28 Jan 2011 00:46:12 +0000 (UTC) commit 0313d97cf24177023cdb6f2e4c54d077c5a775c1 Author: Alex Williamson Date: Wed Sep 29 13:50:39 2010 -0600 vfio: VGA passthrough support(ish) Signed-off-by: Alex Williamson --- diff --git a/Makefile.target b/Makefile.target index c507dd2..cb0cea6 100644 --- a/Makefile.target +++ b/Makefile.target @@ -203,6 +203,7 @@ obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o obj-i386-y += debugcon.o multiboot.o obj-i386-y += pc_piix.o obj-i386-y += vfio.o +obj-$(CONFIG_VFIO_VGA) += vfio-vga.o # shared objects obj-ppc-y = ppc.o diff --git a/configure b/configure index 3bfc5e9..b15e68f 100755 --- a/configure +++ b/configure @@ -322,6 +322,7 @@ user_pie="no" zero_malloc="" trace_backend="nop" trace_file="trace" +vfio_vga="no" # OS specific if check_define __linux__ ; then @@ -718,6 +719,8 @@ for opt do ;; --enable-vhost-net) vhost_net="yes" ;; + --enable-vfio-vga) vfio_vga="yes" + ;; --*dir) ;; *) echo "ERROR: unknown option $opt"; show_help="yes" @@ -907,6 +910,7 @@ echo " --disable-docs disable documentation build" echo " --disable-vhost-net disable vhost-net acceleration support" echo " --enable-vhost-net enable vhost-net acceleration support" echo " --trace-backend=B Trace backend nop simple ust" +echo " --enable-vfio-vga enable vfio VGA passthrough support" echo " --trace-file=NAME Full PATH,NAME of file to store traces" echo " Default:trace-" echo "" @@ -2240,6 +2244,7 @@ echo "preadv support $preadv" echo "fdatasync $fdatasync" echo "uuid support $uuid" echo "vhost-net support $vhost_net" +echo "vfio-vga support $vfio_vga" echo "Trace backend $trace_backend" echo "Trace output file $trace_file-" @@ -2762,6 +2767,9 @@ case "$target_arch2" in if test "$xen" = "yes" -a "$target_softmmu" = "yes" ; then echo "CONFIG_XEN=y" >> $config_target_mak fi + if test $vfio_vga = "yes" ; then + echo "CONFIG_VFIO_VGA=y" >> $config_host_mak + fi esac case "$target_arch2" in i386|x86_64|ppcemb|ppc|ppc64|s390x) diff --git a/hw/vfio-vga.c b/hw/vfio-vga.c new file mode 100644 index 0000000..5c1899c --- /dev/null +++ b/hw/vfio-vga.c @@ -0,0 +1,291 @@ +/* + * vfio VGA device assignment support + * + * Copyright Red Hat, Inc. 2010 + * + * Authors: + * Alex Williamson + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Based on qemu-kvm device-assignment: + * Adapted for KVM by Qumranet. + * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com) + * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com) + * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com) + * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) + * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) + */ + +#include +#include +#include +#include +#include +#include +#include "event_notifier.h" +#include "hw.h" +#include "memory.h" +#include "monitor.h" +#include "pc.h" +#include "qemu-error.h" +#include "sysemu.h" +#include "vfio.h" +#include +#include +#include +#include "linux-vfio.h" + +//#define DEBUG_VFIO_VGA +#ifdef DEBUG_VFIO_VGA +#define DPRINTF(fmt, ...) \ + do { printf("vfio-vga: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +/* + * VGA setup + */ +static void vfio_vga_write(VFIODevice *vdev, uint32_t addr, + uint32_t val, int len) +{ + DPRINTF("%s 0x%x %d - 0x%x\n", __func__, 0xa0000 + addr, len, val); + switch (len) { + case 1: + *(uint8_t *)(vdev->vga_mmio + addr) = (uint8_t)val; + break; + case 2: + *(uint16_t *)(vdev->vga_mmio + addr) = (uint16_t)val; + break; + case 4: + *(uint32_t *)(vdev->vga_mmio + addr) = val; + break; + } +} + +static void vfio_vga_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + vfio_vga_write(opaque, addr, val, 1); +} + +static void vfio_vga_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + vfio_vga_write(opaque, addr, val, 2); +} + +static void vfio_vga_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + vfio_vga_write(opaque, addr, val, 4); +} + +static CPUWriteMemoryFunc * const vfio_vga_writes[] = { + &vfio_vga_writeb, + &vfio_vga_writew, + &vfio_vga_writel +}; + +static uint32_t vfio_vga_read(VFIODevice *vdev, uint32_t addr, int len) +{ + uint32_t val = 0xffffffff; + switch (len) { + case 1: + val = (uint32_t)*(uint8_t *)(vdev->vga_mmio + addr); + break; + case 2: + val = (uint32_t)*(uint16_t *)(vdev->vga_mmio + addr); + break; + case 4: + val = *(uint32_t *)(vdev->vga_mmio + addr); + break; + } + DPRINTF("%s 0x%x %d = 0x%x\n", __func__, 0xa0000 + addr, len, val); + return val; +} + +static uint32_t vfio_vga_readb(void *opaque, target_phys_addr_t addr) +{ + return vfio_vga_read(opaque, addr, 1); +} + +static uint32_t vfio_vga_readw(void *opaque, target_phys_addr_t addr) +{ + return vfio_vga_read(opaque, addr, 2); +} + +static uint32_t vfio_vga_readl(void *opaque, target_phys_addr_t addr) +{ + return vfio_vga_read(opaque, addr, 4); +} + +static CPUReadMemoryFunc * const vfio_vga_reads[] = { + &vfio_vga_readb, + &vfio_vga_readw, + &vfio_vga_readl +}; + +static void vfio_vga_out(VFIODevice *vdev, uint32_t addr, uint32_t val, int len) +{ + DPRINTF("%s 0x%x %d - 0x%x\n", __func__, addr, len, val); + ioperm(0x3b0, 0x30, 1); /* XXX fix me */ + switch (len) { + case 1: + outb(val, addr); + break; + case 2: + outw(val, addr); + break; + case 4: + outl(val, addr); + break; + } +} + +static void vfio_vga_outb(void *opaque, uint32_t addr, uint32_t val) +{ + vfio_vga_out(opaque, addr, val, 1); +} + +static void vfio_vga_outw(void *opaque, uint32_t addr, uint32_t val) +{ + vfio_vga_out(opaque, addr, val, 2); +} + +static void vfio_vga_outl(void *opaque, uint32_t addr, uint32_t val) +{ + vfio_vga_out(opaque, addr, val, 4); +} + +static uint32_t vfio_vga_in(VFIODevice *vdev, uint32_t addr, int len) +{ + uint32_t val = 0xffffffff; + ioperm(0x3b0, 0x30, 1); /* XXX fix me */ + switch (len) { + case 1: + val = inb(addr); + break; + case 2: + val = inw(addr); + break; + case 4: + val = inl(addr); + break; + } + DPRINTF("%s 0x%x, %d = 0x%x\n", __func__, addr, len, val); + return val; +} + +static uint32_t vfio_vga_inb(void *opaque, uint32_t addr) +{ + return vfio_vga_in(opaque, addr, 1); +} + +static uint32_t vfio_vga_inw(void *opaque, uint32_t addr) +{ + return vfio_vga_in(opaque, addr, 2); +} + +static uint32_t vfio_vga_inl(void *opaque, uint32_t addr) +{ + return vfio_vga_in(opaque, addr, 4); +} + +int vfio_vga_setup(VFIODevice *vdev) +{ + char buf[256]; + int ret; + + if (vga_interface_type != VGA_NONE) { + fprintf(stderr, + "VGA devie assigned without -vga none param, no ISA VGA\n"); + return -1; + } + + vdev->vga_fd = open("/dev/vga_arbiter", O_RDWR); + if (vdev->vga_fd < 0) { + fprintf(stderr, "%s - Failed to open vga arbiter (%s)\n", + __func__, strerror(errno)); + return -1; + } + ret = read(vdev->vga_fd, buf, sizeof(buf)); + if (ret <= 0) { + fprintf(stderr, "%s - Failed to read from vga arbiter (%s)\n", + __func__, strerror(errno)); + close(vdev->vga_fd); + return -1; + } + buf[ret - 1] = 0; + vdev->vga_orig = qemu_strdup(buf); + + snprintf(buf, sizeof(buf), "target PCI:%04x:%02x:%02x.%x", + vdev->host.seg, vdev->host.bus, vdev->host.dev, vdev->host.func); + ret = write(vdev->vga_fd, buf, strlen(buf)); + if (ret != strlen(buf)) { + fprintf(stderr, "%s - Failed to write to vga arbiter (%s)\n", + __func__, strerror(errno)); + close(vdev->vga_fd); + return -1; + } + snprintf(buf, sizeof(buf), "decodes io+mem"); + ret = write(vdev->vga_fd, buf, strlen(buf)); + if (ret != strlen(buf)) { + fprintf(stderr, "%s - Failed to write to vga arbiter (%s)\n", + __func__, strerror(errno)); + close(vdev->vga_fd); + return -1; + } + + vdev->vga_mmio_fd = open("/dev/mem", O_RDWR); + if (vdev->vga_mmio_fd < 0) { + fprintf(stderr, "%s - Failed to open /dev/mem (%s)\n", + __func__, strerror(errno)); + return -1; + } + vdev->vga_mmio = mmap(NULL, 0x40000, PROT_READ | PROT_WRITE, + MAP_SHARED, vdev->vga_mmio_fd, 0xa0000); + if (vdev->vga_mmio == MAP_FAILED) { + fprintf(stderr, "%s - mmap failed (%s)\n", __func__, strerror(errno)); + return -1; + } + +#if 1 + vdev->vga_io = cpu_register_io_memory(vfio_vga_reads, + vfio_vga_writes, vdev); + cpu_register_physical_memory(0xa0000, 0x20000, vdev->vga_io); + qemu_register_coalesced_mmio(0xa0000, 0x20000); +#else + cpu_register_physical_memory(0xa0000, 0x20000, + qemu_ram_map(&vdev->pdev.qdev, "VGA", 0x20000, vdev->vga_mmio)); + qemu_register_coalesced_mmio(0xa0000, 0x20000); +#endif + + register_ioport_write(0x3b0, 0x30, 1, vfio_vga_outb, vdev); + register_ioport_write(0x3b0, 0x30, 2, vfio_vga_outw, vdev); + register_ioport_write(0x3b0, 0x30, 4, vfio_vga_outl, vdev); + register_ioport_read(0x3b0, 0x30, 1, vfio_vga_inb, vdev); + register_ioport_read(0x3b0, 0x30, 2, vfio_vga_inw, vdev); + register_ioport_read(0x3b0, 0x30, 4, vfio_vga_inl, vdev); + if (ioperm(0x3b0, 0x30, 1)) { + fprintf(stderr, "%s - ioperm failed (%s)\n", __func__, strerror(errno)); + return -1; + } + return 0; +} + +void vfio_vga_exit(VFIODevice *vdev) +{ + if (!vdev->vga_io) + return; + + isa_unassign_ioport(0x3b0, 0x30); + qemu_unregister_coalesced_mmio(0xa0000, 0x20000); + cpu_register_physical_memory(0xa0000, 0x20000, IO_MEM_UNASSIGNED); + cpu_unregister_io_memory(vdev->vga_io); + munmap(vdev->vga_mmio, 0x40000); + close(vdev->vga_mmio_fd); + qemu_free(vdev->vga_orig); + close(vdev->vga_fd); +} + diff --git a/hw/vfio.c b/hw/vfio.c index e2da724..f7c7a42 100644 --- a/hw/vfio.c +++ b/hw/vfio.c @@ -1268,8 +1268,22 @@ static int vfio_initfn(struct PCIDevice *pdev) if (vfio_enable_intx(vdev)) goto out_unmap_iommu; +#ifdef CONFIG_VFIO_VGA + { + uint16_t class; + + class = vfio_pci_read_config(&vdev->pdev, PCI_CLASS_DEVICE, 2); + if (class == PCI_CLASS_DISPLAY_VGA && vfio_vga_setup(vdev)) + goto out_vga_fail; + } +#endif + return 0; +#ifdef CONFIG_VFIO_VGA +out_vga_fail: + vfio_disable_intx(vdev); +#endif out_unmap_iommu: vfio_unmap_iommu(vdev); out_unmap_resources: @@ -1290,6 +1304,9 @@ static int vfio_exitfn(struct PCIDevice *pdev) { VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); +#ifdef CONFIG_VFIO_VGA + vfio_vga_exit(vdev); +#endif vfio_disable_intx(vdev); vfio_disable_msi(vdev); vfio_disable_msix(vdev); diff --git a/hw/vfio.h b/hw/vfio.h index b5a0525..c7490b3 100644 --- a/hw/vfio.h +++ b/hw/vfio.h @@ -83,8 +83,20 @@ typedef struct VFIODevice { MSIX msix; int vfiofd; int uiommufd; +#ifdef CONFIG_VFIO_VGA + int vga_io; + int vga_fd; + int vga_mmio_fd; + uint8_t *vga_mmio; + char *vga_orig; +#endif char *vfiofd_name; char *uiommufd_name; } VFIODevice; +#ifdef CONFIG_VFIO_VGA +int vfio_vga_setup(VFIODevice *vdev); +void vfio_vga_exit(VFIODevice *vdev); +#endif + #endif /* __VFIO_H__ */