On Thu, 2011-01-27 at 12:56 +0100, Andrà Weidemann wrote: > Hi Alex, > > On 26.01.2011 06:12, Alex Williamson wrote: > > > So while your initial results are promising, my guess is that you're > > using card specific drivers and still need to consider some of the > > harder problems with generic support for vga assignment. I hacked on > > this for a bit trying to see if I could get vga assignment working > > with the vfio driver. Setting up the legacy access and preventing > > qemu from stealing it back should get you basic vga modes and might > > even allow the option rom to run to initialize the card for pre-boot. > > I was able to get this far on a similar ATI card. I never hard much > > luck with other cards though, and I was never able to get the vesa > > extensions working. Thanks, > > Do you mind sharing these patches? Attached. Alex
commit 0313d97cf24177023cdb6f2e4c54d077c5a775c1 Author: Alex Williamson <alex.williamson@xxxxxxxxxx> Date: Wed Sep 29 13:50:39 2010 -0600 vfio: VGA passthrough support(ish) Signed-off-by: Alex Williamson <alex.williamson@xxxxxxxxxx> --- 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-<pid>" 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-<pid>" @@ -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 <alex.williamson@xxxxxxxxxx> + * + * 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@xxxxxxxxxxxx) + * Copyright (c) 2007, Neocleus, Guy Zana (guy@xxxxxxxxxxxx) + * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@xxxxxxxxxxxx) + * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@xxxxxxxxxx) + * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@xxxxxxxxxx) + */ + +#include <stdio.h> +#include <unistd.h> +#include <sys/io.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#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 <pci/header.h> +#include <pci/types.h> +#include <linux/types.h> +#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__ */