All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/4] vga: new display devices
@ 2014-03-12 12:55 Gerd Hoffmann
  2014-03-12 12:55 ` [Qemu-devel] [PATCH 1/4] vga: allow non-global vmstate Gerd Hoffmann
                   ` (4 more replies)
  0 siblings, 5 replies; 23+ messages in thread
From: Gerd Hoffmann @ 2014-03-12 12:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, Gerd Hoffmann

  Hi,

This patch series adds new display devices.

Number one is secondary-vga.  That is identical to VGA (aka -vga std),
except that it doesn't occupy all the legacy vga stuff (ioports, memory
window @ 0xa0000), so you can have more than one of these in the system.
It has one pci memory bar for the framebuffer and one mmio bar for
registers.  OVMF can drive it.  Doesn't use it as console for some
reason, but initializes it and the linux kernel will see it as efifb.
Cutting edge linux kernels can drive it too (bochs-drm driver in 3.14+).

Number two is virtio-gpu-pci.  That is the virtio graphics card written
by Dave Airlie.  Only 2d for now, 3d support is to be tackled later on.

Linux kernel driver:
  http://www.kraxel.org/cgit/linux/log/?h=virtio-vga-3d.kraxel
  http://cgit.freedesktop.org/~airlied/linux/log/?h=virtio-vga-3d

Number three is virtio-vga.  That is virtio-gpu-pci with all the vga
compatibility fluff added.  Firmware builder has cutting edge
seavgabios.git packages which include a virtio vgabios for it, see
  http://www.kraxel.org/repos/

cheers,
  Gerd

Dave Airlie (2):
  virtio-gpu: v0.3 of the virtio based GPU code.
  virtio-vga: v1

Gerd Hoffmann (2):
  vga: allow non-global vmstate
  vga: add secondary stdvga variant

 Makefile                           |   2 +-
 default-configs/x86_64-softmmu.mak |   2 +
 docs/specs/standard-vga.txt        |  13 +-
 docs/specs/virtio-gpu.txt          |  89 +++++
 hw/display/Makefile.objs           |   4 +
 hw/display/cirrus_vga.c            |   4 +-
 hw/display/qxl.c                   |   2 +-
 hw/display/vga-isa-mm.c            |   2 +-
 hw/display/vga-isa.c               |   2 +-
 hw/display/vga-pci.c               |  63 +++-
 hw/display/vga.c                   |   8 +-
 hw/display/vga_int.h               |   2 +-
 hw/display/virtgpu_hw.h            | 149 ++++++++
 hw/display/virtio-gpu-pci.c        |  80 +++++
 hw/display/virtio-gpu.c            | 689 +++++++++++++++++++++++++++++++++++++
 hw/display/virtio-vga.c            | 169 +++++++++
 hw/display/vmware_vga.c            |   2 +-
 hw/pci/pci.c                       |   2 +
 hw/virtio/virtio-pci.h             |  15 +
 include/hw/pci/pci.h               |   1 +
 include/hw/virtio/virtio-gpu.h     |  91 +++++
 include/sysemu/sysemu.h            |   2 +-
 pc-bios/vgabios-virtio.bin         | Bin 0 -> 40448 bytes
 roms/Makefile                      |   2 +-
 roms/config.vga.virtio             |   6 +
 vl.c                               |  13 +
 26 files changed, 1398 insertions(+), 16 deletions(-)
 create mode 100644 docs/specs/virtio-gpu.txt
 create mode 100644 hw/display/virtgpu_hw.h
 create mode 100644 hw/display/virtio-gpu-pci.c
 create mode 100644 hw/display/virtio-gpu.c
 create mode 100644 hw/display/virtio-vga.c
 create mode 100644 include/hw/virtio/virtio-gpu.h
 create mode 100644 pc-bios/vgabios-virtio.bin
 create mode 100644 roms/config.vga.virtio

-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 1/4] vga: allow non-global vmstate
  2014-03-12 12:55 [Qemu-devel] [PATCH 0/4] vga: new display devices Gerd Hoffmann
@ 2014-03-12 12:55 ` Gerd Hoffmann
  2014-03-12 12:55 ` [Qemu-devel] [PATCH 2/4] vga: add secondary stdvga variant Gerd Hoffmann
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 23+ messages in thread
From: Gerd Hoffmann @ 2014-03-12 12:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, Gerd Hoffmann

Need a way to opt-out from vga.vram being global vmstate, for
secondary vga cards.  Add a bool parameter to vga_common_init
to support this.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/display/cirrus_vga.c | 4 ++--
 hw/display/qxl.c        | 2 +-
 hw/display/vga-isa-mm.c | 2 +-
 hw/display/vga-isa.c    | 2 +-
 hw/display/vga-pci.c    | 2 +-
 hw/display/vga.c        | 4 ++--
 hw/display/vga_int.h    | 2 +-
 hw/display/vmware_vga.c | 2 +-
 8 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
index 0d3127d..d1afc76 100644
--- a/hw/display/cirrus_vga.c
+++ b/hw/display/cirrus_vga.c
@@ -2913,7 +2913,7 @@ static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp)
     ISACirrusVGAState *d = ISA_CIRRUS_VGA(dev);
     VGACommonState *s = &d->cirrus_vga.vga;
 
-    vga_common_init(s, OBJECT(dev));
+    vga_common_init(s, OBJECT(dev), true);
     cirrus_init_common(&d->cirrus_vga, OBJECT(dev), CIRRUS_ID_CLGD5430, 0,
                        isa_address_space(isadev),
                        isa_address_space_io(isadev));
@@ -2960,7 +2960,7 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev)
      int16_t device_id = pc->device_id;
 
      /* setup VGA */
-     vga_common_init(&s->vga, OBJECT(dev));
+     vga_common_init(&s->vga, OBJECT(dev), true);
      cirrus_init_common(s, OBJECT(dev), device_id, 1, pci_address_space(dev),
                         pci_address_space_io(dev));
      s->vga.con = graphic_console_init(DEVICE(dev), 0, s->vga.hw_ops, &s->vga);
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 47bbf1f..e9c54d7 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -2061,7 +2061,7 @@ static int qxl_init_primary(PCIDevice *dev)
     qxl->id = 0;
     qxl_init_ramsize(qxl);
     vga->vram_size_mb = qxl->vga.vram_size >> 20;
-    vga_common_init(vga, OBJECT(dev));
+    vga_common_init(vga, OBJECT(dev), true);
     vga_init(vga, OBJECT(dev),
              pci_address_space(dev), pci_address_space_io(dev), false);
     portio_list_init(qxl_vga_port_list, OBJECT(dev), qxl_vga_portio_list,
diff --git a/hw/display/vga-isa-mm.c b/hw/display/vga-isa-mm.c
index afc46b8..4efc222 100644
--- a/hw/display/vga-isa-mm.c
+++ b/hw/display/vga-isa-mm.c
@@ -132,7 +132,7 @@ int isa_vga_mm_init(hwaddr vram_base,
     s = g_malloc0(sizeof(*s));
 
     s->vga.vram_size_mb = VGA_RAM_SIZE >> 20;
-    vga_common_init(&s->vga, NULL);
+    vga_common_init(&s->vga, NULL, true);
     vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space);
 
     s->vga.con = graphic_console_init(NULL, 0, s->vga.hw_ops, s);
diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c
index 1d9ea6b..2b480bd 100644
--- a/hw/display/vga-isa.c
+++ b/hw/display/vga-isa.c
@@ -56,7 +56,7 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp)
     MemoryRegion *vga_io_memory;
     const MemoryRegionPortio *vga_ports, *vbe_ports;
 
-    vga_common_init(s, OBJECT(dev));
+    vga_common_init(s, OBJECT(dev), true);
     s->legacy_address_space = isa_address_space(isadev);
     vga_io_memory = vga_init_io(s, OBJECT(dev), &vga_ports, &vbe_ports);
     isa_register_portio_list(isadev, 0x3b0, vga_ports, s, "vga");
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
index 574ea0e..ad49156 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -147,7 +147,7 @@ static int pci_std_vga_initfn(PCIDevice *dev)
     VGACommonState *s = &d->vga;
 
     /* vga + console init */
-    vga_common_init(s, OBJECT(dev));
+    vga_common_init(s, OBJECT(dev), true);
     vga_init(s, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev),
              true);
 
diff --git a/hw/display/vga.c b/hw/display/vga.c
index 063319d..8891e0a 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -2252,7 +2252,7 @@ static const GraphicHwOps vga_ops = {
     .text_update = vga_update_text,
 };
 
-void vga_common_init(VGACommonState *s, Object *obj)
+void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate)
 {
     int i, j, v, b;
 
@@ -2289,7 +2289,7 @@ void vga_common_init(VGACommonState *s, Object *obj)
 
     s->is_vbe_vmstate = 1;
     memory_region_init_ram(&s->vram, obj, "vga.vram", s->vram_size);
-    vmstate_register_ram_global(&s->vram);
+    vmstate_register_ram(&s->vram, global_vmstate ? NULL : DEVICE(obj));
     xen_register_framebuffer(&s->vram);
     s->vram_ptr = memory_region_get_ram_ptr(&s->vram);
     s->get_bpp = vga_get_bpp;
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
index e641890..d42ac92 100644
--- a/hw/display/vga_int.h
+++ b/hw/display/vga_int.h
@@ -177,7 +177,7 @@ static inline int c6_to_8(int v)
     return (v << 2) | (b << 1) | b;
 }
 
-void vga_common_init(VGACommonState *s, Object *obj);
+void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate);
 void vga_init(VGACommonState *s, Object *obj, MemoryRegion *address_space,
               MemoryRegion *address_space_io, bool init_vga_ports);
 MemoryRegion *vga_init_io(VGACommonState *s, Object *obj,
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index bd2c108..89f139f 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -1206,7 +1206,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s,
     vmstate_register_ram_global(&s->fifo_ram);
     s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram);
 
-    vga_common_init(&s->vga, OBJECT(dev));
+    vga_common_init(&s->vga, OBJECT(dev), true);
     vga_init(&s->vga, OBJECT(dev), address_space, io, true);
     vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga);
     s->new_depth = 32;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 2/4] vga: add secondary stdvga variant
  2014-03-12 12:55 [Qemu-devel] [PATCH 0/4] vga: new display devices Gerd Hoffmann
  2014-03-12 12:55 ` [Qemu-devel] [PATCH 1/4] vga: allow non-global vmstate Gerd Hoffmann
@ 2014-03-12 12:55 ` Gerd Hoffmann
  2014-03-12 13:11   ` Eric Blake
  2014-03-12 12:55 ` [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code Gerd Hoffmann
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 23+ messages in thread
From: Gerd Hoffmann @ 2014-03-12 12:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, Gerd Hoffmann

Add a standard vga variant which doesn't occupy any legacy
ressources and thus can easily be used as secondary (or legacy-free)
graphics adapter.  Programming must be done using the MMIO bar.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 docs/specs/standard-vga.txt | 13 +++++++---
 hw/display/vga-pci.c        | 61 +++++++++++++++++++++++++++++++++++++++++++++
 hw/display/vga.c            |  4 +++
 3 files changed, 75 insertions(+), 3 deletions(-)

diff --git a/docs/specs/standard-vga.txt b/docs/specs/standard-vga.txt
index 8a4c1e9..f82773e 100644
--- a/docs/specs/standard-vga.txt
+++ b/docs/specs/standard-vga.txt
@@ -5,9 +5,10 @@ QEMU Standard VGA
 Exists in two variants, for isa and pci.
 
 command line switches:
-    -vga std            [ picks isa for -M isapc, otherwise pci ]
-    -device VGA         [ pci variant ]
-    -device isa-vga     [ isa variant ]
+    -vga std               [ picks isa for -M isapc, otherwise pci ]
+    -device VGA            [ pci variant ]
+    -device isa-vga        [ isa variant ]
+    -device secondary-vga  [ legacy-free pci variant ]
 
 
 PCI spec
@@ -31,9 +32,15 @@ PCI ROM Region:
    Holds the vgabios (qemu 0.14+).
 
 
+The legacy-free variant has no ROM and has PCI_CLASS_DISPLAY_OTHER
+instead of PCI_CLASS_DISPLAY_VGA.
+
+
 IO ports used
 -------------
 
+Doesn't apply to the legacy-free pci variant, use the MMIO bar instead.
+
 03c0 - 03df : standard vga ports
 01ce        : bochs vbe interface index port
 01cf        : bochs vbe interface data port (x86 only)
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
index ad49156..0865dc4 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -179,12 +179,51 @@ static int pci_std_vga_initfn(PCIDevice *dev)
     return 0;
 }
 
+static int pci_secondary_vga_initfn(PCIDevice *dev)
+{
+    PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev);
+    VGACommonState *s = &d->vga;
+
+    /* vga + console init */
+    vga_common_init(s, OBJECT(dev), false);
+    s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s);
+
+    /* mmio bar */
+    memory_region_init(&d->mmio, OBJECT(dev), "vga.mmio", 4096);
+    memory_region_init_io(&d->ioport, OBJECT(dev), &pci_vga_ioport_ops, d,
+                          "vga ioports remapped", PCI_VGA_IOPORT_SIZE);
+    memory_region_init_io(&d->bochs, OBJECT(dev), &pci_vga_bochs_ops, d,
+                          "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
+
+    memory_region_add_subregion(&d->mmio, PCI_VGA_IOPORT_OFFSET,
+                                &d->ioport);
+    memory_region_add_subregion(&d->mmio, PCI_VGA_BOCHS_OFFSET,
+                                &d->bochs);
+
+    pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
+    pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
+
+    return 0;
+}
+
+static void pci_secondary_vga_reset(DeviceState *dev)
+{
+    PCIVGAState *d = DO_UPCAST(PCIVGAState, dev.qdev, dev);
+
+    vga_common_reset(&d->vga);
+}
+
 static Property vga_pci_properties[] = {
     DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16),
     DEFINE_PROP_BIT("mmio", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_MMIO, true),
     DEFINE_PROP_END_OF_LIST(),
 };
 
+static Property secondary_pci_properties[] = {
+    DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void vga_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
@@ -201,6 +240,20 @@ static void vga_class_init(ObjectClass *klass, void *data)
     set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
 }
 
+static void secondary_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = pci_secondary_vga_initfn;
+    k->vendor_id = PCI_VENDOR_ID_QEMU;
+    k->device_id = PCI_DEVICE_ID_QEMU_VGA;
+    k->class_id = PCI_CLASS_DISPLAY_OTHER;
+    dc->vmsd = &vmstate_vga_pci;
+    dc->props = secondary_pci_properties;
+    dc->reset = pci_secondary_vga_reset;
+}
+
 static const TypeInfo vga_info = {
     .name          = "VGA",
     .parent        = TYPE_PCI_DEVICE,
@@ -208,9 +261,17 @@ static const TypeInfo vga_info = {
     .class_init    = vga_class_init,
 };
 
+static const TypeInfo secondary_info = {
+    .name          = "secondary-vga",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIVGAState),
+    .class_init    = secondary_class_init,
+};
+
 static void vga_register_types(void)
 {
     type_register_static(&vga_info);
+    type_register_static(&secondary_info);
 }
 
 type_init(vga_register_types)
diff --git a/hw/display/vga.c b/hw/display/vga.c
index 8891e0a..c4c3238 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -171,6 +171,10 @@ static void vga_update_memory_access(VGACommonState *s)
     MemoryRegion *region, *old_region = s->chain4_alias;
     hwaddr base, offset, size;
 
+    if (s->legacy_address_space == NULL) {
+        return;
+    }
+
     s->chain4_alias = NULL;
 
     if ((s->sr[VGA_SEQ_PLANE_WRITE] & VGA_SR02_ALL_PLANES) ==
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code.
  2014-03-12 12:55 [Qemu-devel] [PATCH 0/4] vga: new display devices Gerd Hoffmann
  2014-03-12 12:55 ` [Qemu-devel] [PATCH 1/4] vga: allow non-global vmstate Gerd Hoffmann
  2014-03-12 12:55 ` [Qemu-devel] [PATCH 2/4] vga: add secondary stdvga variant Gerd Hoffmann
@ 2014-03-12 12:55 ` Gerd Hoffmann
  2014-03-12 20:26   ` Michael S. Tsirkin
  2014-03-12 12:55 ` [Qemu-devel] [PATCH 4/4] virtio-vga: v1 Gerd Hoffmann
  2014-03-12 13:55 ` [Qemu-devel] [PATCH 0/4] vga: new display devices Laszlo Ersek
  4 siblings, 1 reply; 23+ messages in thread
From: Gerd Hoffmann @ 2014-03-12 12:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, Gerd Hoffmann, Anthony Liguori, Michael S. Tsirkin

From: Dave Airlie <airlied@redhat.com>

This is the basic virtio-gpu which is

multi-head capable,
ARGB cursor support,
unaccelerated.

Some more info is in docs/specs/virtio-gpu.txt.

Signed-off-by: Dave Airlie <airlied@redhat.com>

changes by kraxel:
 * adapt to changes in master.
 * move from hw/virtio to hw/display.
 * codestyle cleanups.
 * misc minor fixes.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>

virtio-gpu codestyle
---
 default-configs/x86_64-softmmu.mak |   1 +
 docs/specs/virtio-gpu.txt          |  89 +++++
 hw/display/Makefile.objs           |   3 +
 hw/display/virtgpu_hw.h            | 149 ++++++++
 hw/display/virtio-gpu-pci.c        |  80 +++++
 hw/display/virtio-gpu.c            | 689 +++++++++++++++++++++++++++++++++++++
 hw/virtio/virtio-pci.h             |  15 +
 include/hw/pci/pci.h               |   1 +
 include/hw/virtio/virtio-gpu.h     |  91 +++++
 9 files changed, 1118 insertions(+)
 create mode 100644 docs/specs/virtio-gpu.txt
 create mode 100644 hw/display/virtgpu_hw.h
 create mode 100644 hw/display/virtio-gpu-pci.c
 create mode 100644 hw/display/virtio-gpu.c
 create mode 100644 include/hw/virtio/virtio-gpu.h

diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 31bddce..1a00b78 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -9,6 +9,7 @@ CONFIG_VGA_PCI=y
 CONFIG_VGA_ISA=y
 CONFIG_VGA_CIRRUS=y
 CONFIG_VMWARE_VGA=y
+CONFIG_VIRTIO_GPU=y
 CONFIG_VMMOUSE=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
diff --git a/docs/specs/virtio-gpu.txt b/docs/specs/virtio-gpu.txt
new file mode 100644
index 0000000..987d807
--- /dev/null
+++ b/docs/specs/virtio-gpu.txt
@@ -0,0 +1,89 @@
+Initial info on virtio-gpu:
+
+virtio-gpu is a virtio based graphics adapter. The initial version provides support for:
+
+- ARGB Hardware cursors
+- multiple outputs
+
+There are no acceleration features available in the first release of this device, 3D acceleration features will be provided later via an interface to an external renderer.
+
+The virtio-gpu exposes 3 vqs to the guest,
+
+1) control vq - guest->host queue for sending commands and getting responses when required.
+2) cursor vq - guest->host queue for sending cursor position and resource updates
+3) event vq - host->guest queue for sending async events like display topography updates and errors (todo).
+
+How to use the virtio-gpu:
+
+The virtio-gpu is based around the concept of resources private to the host, the guest must DMA transfer into these resources. This is a design requirement in order to interface with future 3D rendering. In the unaccelerated there is no support for DMA transfers from resources, just to them.
+
+Resources are initially simple 2D resources, consisting of a width, height and format along with an identifier. The guest must then attach backing store to the resources in order for DMA transfers to work. This is like a GART in a real GPU.
+
+A typical guest user would create a 2D resource using VIRTGPU_CMD_RESOURCE_CREATE_2D, attach backing store using VIRTGPU_CMD_RESOURCE_ATTACH_BACKING, then attach the resource to a scanout using VIRTGPU_CMD_SET_SCANOUT, then use VIRTGPU_CMD_TRANSFER_SEND_2D to send updates to the resource, and finally VIRTGPU_CMD_RESOURCE_FLUSH to flush the scanout buffers to screen.
+
+Command queue:
+
+VIRTGPU_CMD_GET_DISPLAY_INFO:
+Retrieve the current output configuration.
+
+This sends a response in the same queue slot. The response contains the max number of scanouts the host can support,
+along with a list of per-scanout information. The info contains whether the
+scanout is enabled, what its preferred x, y, width and height are and some future flags.
+
+VIRTGPU_CMD_RESOURCE_CREATE_2D:
+Create a 2D resource on the host.
+
+This creates a 2D resource on the host with the specified width, height and format. Only a small subset of formats are support. The resource ids are generated by the guest.
+
+VIRTGPU_CMD_RESOURCE_UNREF:
+Destroy a resource.
+
+This informs the host that a resource is no longer required by the guest.
+
+VIRTGPU_CMD_SET_SCANOUT:
+Set the scanout parameters for a single output.
+
+This sets the scanout parameters for a single scanout. The resource_id is the
+resource to be scanned out from, along with a rectangle specified by x, y, width and height.
+
+VIRTGPU_CMD_RESOURCE_FLUSH:
+Flush a scanout resource
+
+This flushes a resource to screen, it takes a rectangle and a resource id,
+and flushes any scanouts the resource is being used on.
+
+VIRTGPU_CMD_TRANSFER_TO_HOST_2D:
+Transfer from guest memory to host resource.
+
+This takes a resource id along with an destination offset into the resource,
+and a box to transfer from the host backing for the resource.
+
+VIRTGPU_CMD_RESOURCE_ATTACH_BACKING:
+Assign backing pages to a resource.
+
+This assign an array of guest pages as the backing store for a resource. These
+pages are then used for the transfer operations for that resource from that
+point on.
+
+VIRTGPU_CMD_RESOURCE_DETACH_BACKING:
+Detach backing pages from a resource.
+
+This detaches any backing pages from a resource, to be used in case of
+guest swapping or object destruction.
+
+Cursor queue:
+The cursor queue accepts on structure per vq entry. The cursor is just a standard 2D resource that is 64x64 sized. Transfers are used to upload the cursor contents.
+
+For cursor movement, the cursor_x and cursor_y fields only need to be updated.
+
+For a cursor update where the guest has transferred a new cursor into the resource, the hotspot, cursor id, should be updated, but the generation_id should also be increased by one, so the host knows to refresh its copy of the cursor data from the resource.
+
+Event queue:
+The only current event passed is a message to denote the host wants to update the layout of the screens. It contains the same info as the response to VIRTGPU_CMD_GET_DISPLAY_INFO.
+
+
+
+
+
+
+
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index 7ed76a9..6574830 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -33,3 +33,6 @@ obj-$(CONFIG_CG3) += cg3.o
 obj-$(CONFIG_VGA) += vga.o
 
 common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
+
+obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu.o
+obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu-pci.o
diff --git a/hw/display/virtgpu_hw.h b/hw/display/virtgpu_hw.h
new file mode 100644
index 0000000..0709922
--- /dev/null
+++ b/hw/display/virtgpu_hw.h
@@ -0,0 +1,149 @@
+/*
+ * virtgpu_hw.h - describe the virtgpu hw interface.
+ *
+ * this file is shared with the kernel and follow kernel coding
+ * conventions, we can write a qemu specific one once everything is stable,
+ * but it will make merging things messier.
+ */
+#ifndef VIRTGPU_HW_H
+#define VIRTGPU_HW_H
+
+#define VIRTGPU_CMD_HAS_RESP (1 << 31)
+
+enum virtgpu_ctrl_cmd {
+        VIRTGPU_CMD_NOP,
+        VIRTGPU_CMD_GET_DISPLAY_INFO = (1 | VIRTGPU_CMD_HAS_RESP),
+        /* reserved */
+        VIRTGPU_CMD_RESOURCE_CREATE_2D = 3,
+        VIRTGPU_CMD_RESOURCE_UNREF = 4,
+        VIRTGPU_CMD_SET_SCANOUT = 5,
+        VIRTGPU_CMD_RESOURCE_FLUSH = 6,
+        VIRTGPU_CMD_TRANSFER_TO_HOST_2D = 7,
+        VIRTGPU_CMD_RESOURCE_ATTACH_BACKING = 8,
+        VIRTGPU_CMD_RESOURCE_INVAL_BACKING = 9,
+};
+
+enum virtgpu_ctrl_event {
+        VIRTGPU_EVENT_NOP,
+        VIRTGPU_EVENT_DISPLAY_CHANGE,
+};
+
+/* data passed in the cursor vq */
+struct virtgpu_hw_cursor_page {
+        uint32_t cursor_x, cursor_y;
+        uint32_t cursor_hot_x, cursor_hot_y;
+        uint32_t cursor_id;
+        uint32_t generation_id;
+};
+
+struct virtgpu_resource_unref {
+        uint32_t resource_id;
+};
+
+/* create a simple 2d resource with a format */
+struct virtgpu_resource_create_2d {
+        uint32_t resource_id;
+        uint32_t format;
+        uint32_t width;
+        uint32_t height;
+};
+
+struct virtgpu_set_scanout {
+        uint32_t scanout_id;
+        uint32_t resource_id;
+        uint32_t width;
+        uint32_t height;
+        uint32_t x;
+        uint32_t y;
+};
+
+struct virtgpu_resource_flush {
+        uint32_t resource_id;
+        uint32_t width;
+        uint32_t height;
+        uint32_t x;
+        uint32_t y;
+};
+
+/* simple transfer to_host */
+struct virtgpu_transfer_to_host_2d {
+        uint32_t resource_id;
+        uint32_t offset;
+        uint32_t width;
+        uint32_t height;
+        uint32_t x;
+        uint32_t y;
+};
+
+struct virtgpu_mem_entry {
+        uint64_t addr;
+        uint32_t length;
+        uint32_t pad;
+};
+
+struct virtgpu_resource_attach_backing {
+        uint32_t resource_id;
+        uint32_t nr_entries;
+};
+
+struct virtgpu_resource_inval_backing {
+        uint32_t resource_id;
+};
+
+#define VIRTGPU_MAX_SCANOUTS 16
+struct virtgpu_display_info {
+        uint32_t num_scanouts;
+        struct {
+                uint32_t enabled;
+                uint32_t width;
+                uint32_t height;
+                uint32_t x;
+                uint32_t y;
+                uint32_t flags;
+        } pmodes[VIRTGPU_MAX_SCANOUTS];
+};
+
+struct virtgpu_command {
+        uint32_t type;
+        uint32_t flags;
+        uint64_t rsvd;
+        union virtgpu_cmds {
+                struct virtgpu_resource_create_2d resource_create_2d;
+                struct virtgpu_resource_unref resource_unref;
+                struct virtgpu_resource_flush resource_flush;
+                struct virtgpu_set_scanout set_scanout;
+                struct virtgpu_transfer_to_host_2d transfer_to_host_2d;
+                struct virtgpu_resource_attach_backing resource_attach_backing;
+                struct virtgpu_resource_inval_backing resource_inval_backing;
+        } u;
+};
+
+struct virtgpu_response {
+        uint32_t type;
+        uint32_t flags;
+        union virtgpu_resps {
+                struct virtgpu_display_info display_info;
+        } u;
+};
+
+struct virtgpu_event {
+        uint32_t type;
+        uint32_t err_code;
+        union virtgpu_events {
+                struct virtgpu_display_info display_info;
+        } u;
+};
+
+/* simple formats for fbcon/X use */
+enum virtgpu_formats {
+       VIRGL_FORMAT_B8G8R8A8_UNORM          = 1,
+       VIRGL_FORMAT_B8G8R8X8_UNORM          = 2,
+       VIRGL_FORMAT_A8R8G8B8_UNORM          = 3,
+       VIRGL_FORMAT_X8R8G8B8_UNORM          = 4,
+
+       VIRGL_FORMAT_B5G5R5A1_UNORM          = 5,
+
+       VIRGL_FORMAT_R8_UNORM                = 64,
+};
+
+#endif
diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c
new file mode 100644
index 0000000..642b293
--- /dev/null
+++ b/hw/display/virtio-gpu-pci.c
@@ -0,0 +1,80 @@
+/*
+ * Virtio video device
+ *
+ * Copyright Red Hat
+ *
+ * Authors:
+ *  Dave Airlie
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include "hw/pci/pci.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/virtio-pci.h"
+#include "hw/virtio/virtio-gpu.h"
+
+static Property virtio_gpu_pci_properties[] = {
+    DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
+    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static int virtio_gpu_pci_init(VirtIOPCIProxy *vpci_dev)
+{
+    VirtIOGPUPCI *vgpu = VIRTIO_GPU_PCI(vpci_dev);
+    DeviceState *vdev = DEVICE(&vgpu->vdev);
+    VirtIOGPU *g = &vgpu->vdev;
+    int i;
+
+    qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+    if (qdev_init(vdev) < 0) {
+        return -1;
+    }
+
+    for (i = 0; i < g->conf.max_outputs; i++) {
+        g->con[i] = graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g);
+        if (i > 0) {
+            dpy_gfx_replace_surface(g->con[i], NULL);
+        }
+    }
+    return 0;
+}
+
+static void virtio_gpu_pci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+    PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+    set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+    dc->props = virtio_gpu_pci_properties;
+    k->init = virtio_gpu_pci_init;
+    pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_GPU;
+    pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+    pcidev_k->class_id = PCI_CLASS_DISPLAY_OTHER;
+}
+
+static void virtio_gpu_initfn(Object *obj)
+{
+    VirtIOGPUPCI *dev = VIRTIO_GPU_PCI(obj);
+    object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU);
+    object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+}
+
+static const TypeInfo virtio_gpu_pci_info = {
+    .name = TYPE_VIRTIO_GPU_PCI,
+    .parent = TYPE_VIRTIO_PCI,
+    .instance_size = sizeof(VirtIOGPUPCI),
+    .instance_init = virtio_gpu_initfn,
+    .class_init = virtio_gpu_pci_class_init,
+};
+
+static void virtio_gpu_pci_register_types(void)
+{
+    type_register_static(&virtio_gpu_pci_info);
+}
+type_init(virtio_gpu_pci_register_types)
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
new file mode 100644
index 0000000..cf7c0a2
--- /dev/null
+++ b/hw/display/virtio-gpu.c
@@ -0,0 +1,689 @@
+/*
+ * Virtio video device
+ *
+ * Copyright Red Hat
+ *
+ * Authors:
+ *  Dave Airlie
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "qemu/iov.h"
+#include "ui/console.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-gpu.h"
+#include "hw/virtio/virtio-bus.h"
+
+#include "virtgpu_hw.h"
+
+static struct virtgpu_simple_resource *virtgpu_find_resource(VirtIOGPU *g,
+                                                          uint32_t resource_id);
+
+static void update_cursor_data(VirtIOGPU *g,
+                               struct virtgpu_simple_resource *res)
+{
+    uint32_t pixels;
+    int i;
+
+    pixels = g->current_cursor->width * g->current_cursor->height;
+    for (i = 0; i < pixels; i++) {
+        uint32_t value = ((uint32_t *)pixman_image_get_data(res->image))[i];
+        g->current_cursor->data[i] = value;
+    }
+}
+
+static void update_cursor(VirtIOGPU *g, struct virtgpu_hw_cursor_page *cursor)
+{
+    if (g->cursor_generation_id != cursor->generation_id) {
+
+        if (!g->current_cursor) {
+            g->current_cursor = cursor_alloc(64, 64);
+        }
+
+        g->current_cursor->hot_x = cursor->cursor_hot_x;
+        g->current_cursor->hot_y = cursor->cursor_hot_y;
+
+        if (cursor->cursor_id > 0) {
+            struct virtgpu_simple_resource *res;
+
+            res = virtgpu_find_resource(g, cursor->cursor_id);
+            if (res) {
+                update_cursor_data(g, res);
+            }
+        }
+        g->cursor_generation_id = cursor->generation_id;
+        g->current_cursor_id = cursor->cursor_id;
+        dpy_cursor_define(g->con[0], g->current_cursor);
+    }
+
+    dpy_mouse_set(g->con[0], cursor->cursor_x, cursor->cursor_y,
+                  cursor->cursor_id ? 1 : 0);
+}
+
+static void virtio_gpu_get_config(VirtIODevice *vdev, uint8_t *config)
+{
+}
+
+static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config)
+{
+}
+
+static uint32_t virtio_gpu_get_features(VirtIODevice *vdev, uint32_t features)
+{
+    return features;
+}
+
+static void virtio_gpu_set_features(VirtIODevice *vdev, uint32_t features)
+{
+}
+
+static struct virtgpu_simple_resource *virtgpu_find_resource(VirtIOGPU *g,
+                                                          uint32_t resource_id)
+{
+    struct virtgpu_simple_resource *res;
+
+    QLIST_FOREACH(res, &g->reslist, next) {
+        if (res->resource_id == resource_id) {
+            return res;
+        }
+    }
+    return NULL;
+}
+
+static void virtgpu_fill_display_info(VirtIOGPU *g,
+                                      struct virtgpu_display_info *dpy_info)
+{
+    int i;
+
+    memset(dpy_info, 0, sizeof(*dpy_info));
+    dpy_info->num_scanouts = g->conf.max_outputs;
+    for (i = 0; i < g->conf.max_outputs; i++) {
+        if (g->enabled_output_bitmask & (1 << i)) {
+            dpy_info->pmodes[i].enabled = 1;
+            dpy_info->pmodes[i].width = g->req_state[i].width;
+            dpy_info->pmodes[i].height = g->req_state[i].height;
+        }
+    }
+}
+
+static void virtgpu_get_display_info(VirtIOGPU *g,
+                                     struct iovec *iov,
+                                     unsigned int iov_cnt)
+{
+    struct virtgpu_response resp;
+
+    resp.type = VIRTGPU_CMD_GET_DISPLAY_INFO;
+    resp.flags = 0;
+    virtgpu_fill_display_info(g, &resp.u.display_info);
+
+    iov_from_buf(iov, iov_cnt, 0, &resp, sizeof(struct virtgpu_response));
+}
+
+static pixman_format_code_t get_pixman_format(uint32_t virtgpu_format)
+{
+    switch (virtgpu_format) {
+    case VIRGL_FORMAT_B8G8R8X8_UNORM:
+        return PIXMAN_x8r8g8b8;
+    case VIRGL_FORMAT_B8G8R8A8_UNORM:
+        return PIXMAN_a8r8g8b8;
+    default:
+        assert(0);
+        break;
+    }
+    return 0;
+}
+
+static void virtgpu_resource_create_2d(VirtIOGPU *g,
+                                       struct virtgpu_resource_create_2d *c2d)
+{
+    pixman_format_code_t pformat;
+    struct virtgpu_simple_resource *res;
+
+    res = g_new0(struct virtgpu_simple_resource, 1);
+    if (!res) {
+        return;
+    }
+
+    res->width = c2d->width;
+    res->height = c2d->height;
+    res->format = c2d->format;
+    res->resource_id = c2d->resource_id;
+
+    pformat = get_pixman_format(c2d->format);
+    res->image = pixman_image_create_bits(pformat,
+                                          c2d->width,
+                                          c2d->height,
+                                          NULL, 0);
+
+    QLIST_INSERT_HEAD(&g->reslist, res, next);
+}
+
+static void virtgpu_resource_destroy(struct virtgpu_simple_resource *res)
+{
+    pixman_image_unref(res->image);
+    QLIST_REMOVE(res, next);
+    g_free(res);
+}
+
+static void virtgpu_resource_unref(VirtIOGPU *g,
+                                   uint32_t resource_id)
+{
+    struct virtgpu_simple_resource *res = virtgpu_find_resource(g, resource_id);
+
+    if (!res) {
+        return;
+    }
+    virtgpu_resource_destroy(res);
+}
+
+static void virtgpu_transfer_to_host_2d(VirtIOGPU *g,
+                                     struct virtgpu_transfer_to_host_2d *t2d)
+{
+    struct virtgpu_simple_resource *res =
+        virtgpu_find_resource(g, t2d->resource_id);
+    int h;
+    uint32_t src_offset, dst_offset, stride;
+    int bpp;
+    pixman_format_code_t format;
+
+    if (!res || !res->iov) {
+        return;
+    }
+
+    format = pixman_image_get_format(res->image);
+    bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
+    stride = pixman_image_get_stride(res->image);
+
+    if (t2d->offset || t2d->x || t2d->y ||
+        t2d->width != pixman_image_get_width(res->image)) {
+        for (h = 0; h < t2d->height; h++) {
+            src_offset = t2d->offset + stride * h;
+            dst_offset = (t2d->y + h) * stride + (t2d->x * bpp);
+
+            iov_to_buf(res->iov, res->iov_cnt, src_offset,
+                       (uint8_t *)pixman_image_get_data(res->image)
+                       + dst_offset, t2d->width * bpp);
+        }
+    } else {
+        iov_to_buf(res->iov, res->iov_cnt, 0,
+                   pixman_image_get_data(res->image),
+                   pixman_image_get_stride(res->image)
+                   * pixman_image_get_height(res->image));
+    }
+}
+
+static void virtgpu_resource_flush(VirtIOGPU *g,
+                                   struct virtgpu_resource_flush *rf)
+{
+    struct virtgpu_simple_resource *res =
+        virtgpu_find_resource(g, rf->resource_id);
+    pixman_region16_t flush_region;
+    int i;
+
+    pixman_region_init_rect(&flush_region,
+                            rf->x, rf->y, rf->width, rf->height);
+    for (i = 0; i < VIRTGPU_MAX_SCANOUT; i++) {
+        struct virtgpu_scanout *scanout;
+        pixman_region16_t region, finalregion;
+        pixman_box16_t *extents;
+
+        if (!(res->scanout_bitmask & (1 << i))) {
+            continue;
+        }
+        scanout = &g->scanout[i];
+
+        pixman_region_init(&finalregion);
+        pixman_region_init_rect(&region, scanout->x, scanout->y,
+                                scanout->width, scanout->height);
+
+        pixman_region_intersect(&finalregion, &flush_region, &region);
+        pixman_region_translate(&finalregion, -scanout->x, -scanout->y);
+        extents = pixman_region_extents(&finalregion);
+        /* work out the area we need to update for each console */
+        dpy_gfx_update(g->con[i], extents->x1, extents->y1,
+                       extents->x2 - extents->x1,
+                       extents->y2 - extents->y1);
+
+        pixman_region_fini(&region);
+        pixman_region_fini(&finalregion);
+    }
+    pixman_region_fini(&flush_region);
+}
+
+static void virtgpu_set_scanout(VirtIOGPU *g,
+                                struct virtgpu_set_scanout *ss)
+{
+    struct virtgpu_simple_resource *res =
+        virtgpu_find_resource(g, ss->resource_id);
+    struct virtgpu_scanout *scanout;
+    pixman_format_code_t format;
+    uint32_t offset;
+    int bpp;
+
+    g->enable = 1;
+    if (ss->resource_id == 0) {
+        scanout = &g->scanout[ss->scanout_id];
+        if (g->scanout[ss->scanout_id].resource_id) {
+            res = virtgpu_find_resource(g, scanout->resource_id);
+            if (res) {
+                res->scanout_bitmask &= ~(1 << ss->scanout_id);
+            }
+        }
+        if (ss->scanout_id == 0) {
+            return;
+        }
+        dpy_gfx_replace_surface(g->con[ss->scanout_id], NULL);
+        scanout->ds = NULL;
+        scanout->width = 0;
+        scanout->height = 0;
+
+#if 0
+        dpy_notify_state(g->con[ss->scanout_id], 0, 0, 0, 0);
+#endif
+        return;
+    }
+    /* create a surface for this scanout */
+
+    if (ss->scanout_id >= VIRTGPU_MAX_SCANOUT) {
+        fprintf(stderr, "set scanout for non-0 surface %d\n", ss->scanout_id);
+        return;
+    }
+    scanout = &g->scanout[ss->scanout_id];
+
+    format = pixman_image_get_format(res->image);
+    bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
+    offset = (ss->x * bpp) + ss->y * pixman_image_get_stride(res->image);
+
+#if 0
+    dpy_notify_state(g->con[ss->scanout_id], ss->x, ss->y,
+                     ss->width, ss->height);
+#endif
+    if (!scanout->ds || surface_data(scanout->ds)
+        != ((uint8_t *)pixman_image_get_data(res->image) + offset) ||
+        scanout->width != ss->width ||
+        scanout->height != ss->height) {
+        /* realloc the surface ptr */
+        scanout->ds = qemu_create_displaysurface_from
+            (ss->width, ss->height, 32,
+             pixman_image_get_stride(res->image),
+             (uint8_t *)pixman_image_get_data(res->image) + offset, FALSE);
+        if (!scanout->ds) {
+            return;
+        }
+        dpy_gfx_replace_surface(g->con[ss->scanout_id], scanout->ds);
+    }
+
+    res->scanout_bitmask |= (1 << ss->scanout_id);
+    scanout->resource_id = ss->resource_id;
+    scanout->x = ss->x;
+    scanout->y = ss->y;
+    scanout->width = ss->width;
+    scanout->height = ss->height;
+}
+
+static void
+virtgpu_resource_attach_backing(VirtIOGPU *g,
+                                struct virtgpu_resource_attach_backing *att_rb,
+                                struct iovec *iov,
+                                unsigned int iov_cnt)
+{
+    struct virtgpu_simple_resource *res =
+        virtgpu_find_resource(g, att_rb->resource_id);
+    uint32_t gsize = iov_size(iov, iov_cnt);
+    struct iovec *res_iovs;
+    int i;
+    void *data;
+
+    if (!res) {
+        return;
+    }
+
+#if 0
+    res_iovs = g_new0(struct iovec, att_rb->nr_entries);
+#else
+    res_iovs = g_malloc0(sizeof(struct iovec) * att_rb->nr_entries);
+#endif
+
+    if (iov_cnt > 1) {
+        data = g_malloc(gsize);
+        iov_to_buf(iov, iov_cnt, 0, data, gsize);
+    } else {
+        data = iov[0].iov_base;
+    }
+
+    for (i = 0; i < att_rb->nr_entries; i++) {
+        struct virtgpu_mem_entry *ent = ((struct virtgpu_mem_entry *)data) + i;
+        hwaddr len;
+        res_iovs[i].iov_len = ent->length;
+
+        len = ent->length;
+        res_iovs[i].iov_base = cpu_physical_memory_map(ent->addr, &len, 1);
+        if (!res_iovs[i].iov_base || len != ent->length) {
+            fprintf(stderr, "virtgp: trying to map MMIO memory");
+            exit(1);
+        }
+    }
+
+    res->iov = res_iovs;
+    res->iov_cnt = att_rb->nr_entries;
+
+    if (iov_cnt > 1) {
+        g_free(data);
+    }
+}
+
+static void virtgpu_resource_inval_backing(VirtIOGPU *g,
+                                           uint32_t resource_id)
+{
+    struct virtgpu_simple_resource *res
+        = virtgpu_find_resource(g, resource_id);
+    int i;
+
+    if (!res || !res->iov) {
+        return;
+    }
+
+    for (i = 0; i < res->iov_cnt; i++) {
+        cpu_physical_memory_unmap(res->iov[i].iov_base, res->iov[i].iov_len, 1,
+                                  res->iov[i].iov_len);
+    }
+    g_free(res->iov);
+    res->iov_cnt = 0;
+    res->iov = NULL;
+}
+
+static void virtio_gpu_process_cmd(VirtIOGPU *g,
+                                   struct virtgpu_command *cmd,
+                                   struct iovec *iov,
+                                   unsigned int iov_cnt)
+{
+    switch (cmd->type) {
+    case VIRTGPU_CMD_GET_DISPLAY_INFO:
+        if (iov_cnt < 1) {
+            return;
+        }
+        virtgpu_get_display_info(g, iov, iov_cnt);
+        break;
+    case VIRTGPU_CMD_RESOURCE_CREATE_2D:
+        virtgpu_resource_create_2d(g, &cmd->u.resource_create_2d);
+        break;
+    case VIRTGPU_CMD_RESOURCE_UNREF:
+        virtgpu_resource_unref(g, cmd->u.resource_unref.resource_id);
+        break;
+    case VIRTGPU_CMD_RESOURCE_FLUSH:
+        virtgpu_resource_flush(g, &cmd->u.resource_flush);
+        break;
+    case VIRTGPU_CMD_TRANSFER_TO_HOST_2D:
+        virtgpu_transfer_to_host_2d(g, &cmd->u.transfer_to_host_2d);
+        break;
+    case VIRTGPU_CMD_SET_SCANOUT:
+        virtgpu_set_scanout(g, &cmd->u.set_scanout);
+        break;
+    case VIRTGPU_CMD_RESOURCE_ATTACH_BACKING:
+        virtgpu_resource_attach_backing(g, &cmd->u.resource_attach_backing,
+                                        iov, iov_cnt);
+        break;
+    case VIRTGPU_CMD_RESOURCE_INVAL_BACKING:
+        virtgpu_resource_inval_backing
+            (g, cmd->u.resource_inval_backing.resource_id);
+        break;
+    case VIRTGPU_CMD_NOP:
+        break;
+    default:
+        fprintf(stderr, "got bad command from host %d\n", cmd->type);
+        break;
+    }
+}
+
+static void virtio_gpu_handle_ctrl_cb(VirtIODevice *vdev, VirtQueue *vq)
+{
+    VirtIOGPU *g = VIRTIO_GPU(vdev);
+    qemu_bh_schedule(g->ctrl_bh);
+}
+
+static void virtio_gpu_handle_cursor_cb(VirtIODevice *vdev, VirtQueue *vq)
+{
+    VirtIOGPU *g = VIRTIO_GPU(vdev);
+    qemu_bh_schedule(g->cursor_bh);
+}
+
+static void virtio_gpu_handle_event_cb(VirtIODevice *vdev, VirtQueue *vq)
+{
+}
+
+
+static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+{
+    VirtIOGPU *g = VIRTIO_GPU(vdev);
+    struct iovec *iov;
+    VirtQueueElement elem;
+    struct virtgpu_command cmd;
+    size_t s;
+    unsigned int iov_cnt;
+
+    if (!virtio_queue_ready(vq)) {
+        return;
+    }
+    while (virtqueue_pop(vq, &elem)) {
+
+        iov = elem.out_sg;
+        iov_cnt = elem.out_num;
+
+        s = iov_to_buf(iov, iov_cnt, 0, &cmd, sizeof(cmd));
+        if (s != sizeof(cmd)) {
+            fprintf(stderr, "error\n");
+        } else {
+            if (elem.in_num > 0) {
+                virtio_gpu_process_cmd(g, &cmd, elem.in_sg, elem.in_num);
+            } else {
+                virtio_gpu_process_cmd(g, &cmd, &iov[1], iov_cnt - 1);
+            }
+        }
+        virtqueue_push(vq, &elem, 0);
+        virtio_notify(vdev, vq);
+    }
+}
+
+static void virtio_gpu_ctrl_bh(void *opaque)
+{
+    VirtIOGPU *g = opaque;
+    virtio_gpu_handle_ctrl(&g->parent_obj, g->ctrl_vq);
+}
+
+static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq)
+{
+    VirtIOGPU *g = VIRTIO_GPU(vdev);
+    VirtQueueElement elem;
+    size_t s;
+    struct virtgpu_hw_cursor_page cursor_info;
+
+    if (!virtio_queue_ready(vq)) {
+        return;
+    }
+    while (virtqueue_pop(vq, &elem)) {
+        s = iov_to_buf(elem.out_sg, elem.out_num, 0,
+                       &cursor_info, sizeof(cursor_info));
+        if (s != sizeof(cursor_info)) {
+            fprintf(stderr, "error\n");
+        } else {
+            update_cursor(g, &cursor_info);
+        }
+        virtqueue_push(vq, &elem, 0);
+        virtio_notify(vdev, vq);
+    }
+}
+
+static void virtio_gpu_cursor_bh(void *opaque)
+{
+    VirtIOGPU *g = opaque;
+    virtio_gpu_handle_cursor(&g->parent_obj, g->cursor_vq);
+}
+
+static int virtio_gpu_send_event(VirtIOGPU *g, struct virtgpu_event *event)
+{
+    VirtQueueElement elem;
+
+    if (!virtio_queue_ready(g->event_vq)) {
+        return -1;
+    }
+
+    if (!virtqueue_pop(g->event_vq, &elem)) {
+        return -1;
+    }
+
+    iov_from_buf(elem.in_sg, elem.in_num, 0, event,
+                 sizeof(struct virtgpu_event));
+
+    virtqueue_push(g->event_vq, &elem, sizeof(struct virtgpu_event));
+    virtio_notify(&g->parent_obj, g->event_vq);
+    return 0;
+}
+
+static void virtio_gpu_invalidate_display(void *opaque)
+{
+}
+
+static void virtio_gpu_update_display(void *opaque)
+{
+}
+
+static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata)
+{
+}
+
+static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
+{
+    VirtIOGPU *g = opaque;
+    struct virtgpu_event event;
+
+    if (idx > g->conf.max_outputs) {
+        return -1;
+    }
+
+    g->req_state[idx].x = info->xoff;
+    g->req_state[idx].y = info->yoff;
+    g->req_state[idx].width = info->width;
+    g->req_state[idx].height = info->height;
+
+    if (info->width && info->height) {
+        g->enabled_output_bitmask |= (1 << idx);
+    } else {
+        g->enabled_output_bitmask &= ~(1 << idx);
+    }
+
+    /* send event to guest */
+    event.type = VIRTGPU_EVENT_DISPLAY_CHANGE;
+    event.err_code = 0;
+    virtgpu_fill_display_info(g, &event.u.display_info);
+    virtio_gpu_send_event(g, &event);
+    return 0;
+}
+
+const GraphicHwOps virtio_gpu_ops = {
+    .invalidate = virtio_gpu_invalidate_display,
+    .gfx_update = virtio_gpu_update_display,
+    .text_update = virtio_gpu_text_update,
+    .ui_info = virtio_gpu_ui_info,
+};
+
+static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
+    VirtIOGPU *g = VIRTIO_GPU(qdev);
+
+    g->cursor_generation_id = 0xffffffff;
+    g->config_size = 0;
+    virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU,
+                g->config_size);
+
+    g->req_state[0].width = 1024;
+    g->req_state[0].height = 768;
+
+    g->ctrl_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb);
+    g->cursor_vq = virtio_add_queue(vdev, 256, virtio_gpu_handle_cursor_cb);
+
+    g->ctrl_bh = qemu_bh_new(virtio_gpu_ctrl_bh, g);
+    g->cursor_bh = qemu_bh_new(virtio_gpu_cursor_bh, g);
+
+    g->event_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_event_cb);
+
+    g->enabled_output_bitmask = 1;
+    g->qdev = qdev;
+}
+
+static void virtio_gpu_instance_init(Object *obj)
+{
+}
+
+static void virtio_gpu_reset(VirtIODevice *vdev)
+{
+    VirtIOGPU *g = VIRTIO_GPU(vdev);
+    struct virtgpu_simple_resource *res, *tmp;
+    int i;
+
+    g->cursor_generation_id = 0xffffffff;
+    g->current_cursor_id = 0;
+    g->enable = 0;
+
+    QLIST_FOREACH_SAFE(res, &g->reslist, next, tmp) {
+        virtgpu_resource_destroy(res);
+    }
+    for (i = 0; i < g->conf.max_outputs; i++) {
+        g->req_state[i].x = 0;
+        g->req_state[i].y = 0;
+        if (i == 0) {
+            g->req_state[0].width = 1024;
+            g->req_state[0].height = 768;
+        } else {
+            g->req_state[i].width = 0;
+            g->req_state[i].height = 0;
+        }
+        g->scanout[i].resource_id = 0;
+        g->scanout[i].width = 0;
+        g->scanout[i].height = 0;
+        g->scanout[i].x = 0;
+        g->scanout[i].y = 0;
+        g->scanout[i].ds = NULL;
+    }
+    g->enabled_output_bitmask = 1;
+}
+
+static Property virtio_gpu_properties[] = {
+    DEFINE_VIRTIO_GPU_PROPERTIES(VirtIOGPU, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_gpu_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+    vdc->realize = virtio_gpu_device_realize;
+    vdc->get_config = virtio_gpu_get_config;
+    vdc->set_config = virtio_gpu_set_config;
+    vdc->get_features = virtio_gpu_get_features;
+    vdc->set_features = virtio_gpu_set_features;
+
+    vdc->reset = virtio_gpu_reset;
+
+    dc->props = virtio_gpu_properties;
+}
+
+static const TypeInfo virtio_gpu_info = {
+    .name = TYPE_VIRTIO_GPU,
+    .parent = TYPE_VIRTIO_DEVICE,
+    .instance_size = sizeof(VirtIOGPU),
+    .instance_init = virtio_gpu_instance_init,
+    .class_init = virtio_gpu_class_init,
+};
+
+static void virtio_register_types(void)
+{
+    type_register_static(&virtio_gpu_info);
+}
+
+type_init(virtio_register_types)
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index dc332ae..fecb801 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -24,6 +24,7 @@
 #include "hw/virtio/virtio-balloon.h"
 #include "hw/virtio/virtio-bus.h"
 #include "hw/virtio/virtio-9p.h"
+#include "hw/virtio/virtio-gpu.h"
 #ifdef CONFIG_VIRTFS
 #include "hw/9pfs/virtio-9p.h"
 #endif
@@ -39,6 +40,7 @@ typedef struct VirtIOSerialPCI VirtIOSerialPCI;
 typedef struct VirtIONetPCI VirtIONetPCI;
 typedef struct VHostSCSIPCI VHostSCSIPCI;
 typedef struct VirtIORngPCI VirtIORngPCI;
+typedef struct VirtIOGPUPCI VirtIOGPUPCI;
 
 /* virtio-pci-bus */
 
@@ -199,6 +201,19 @@ struct VirtIORngPCI {
     VirtIORNG vdev;
 };
 
+/*
+ * virtio-gpu-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci"
+#define VIRTIO_GPU_PCI(obj) \
+        OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI)
+
+struct VirtIOGPUPCI {
+    VirtIOPCIProxy parent_obj;
+    VirtIOGPU vdev;
+};
+
+
 /* Virtio ABI version, if we increment this, we break the guest driver. */
 #define VIRTIO_PCI_ABI_VERSION          0
 
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index 693dd6b..7fc96d2 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -80,6 +80,7 @@
 #define PCI_DEVICE_ID_VIRTIO_SCSI        0x1004
 #define PCI_DEVICE_ID_VIRTIO_RNG         0x1005
 #define PCI_DEVICE_ID_VIRTIO_9P          0x1009
+#define PCI_DEVICE_ID_VIRTIO_GPU         0x1010
 
 #define PCI_VENDOR_ID_REDHAT             0x1b36
 #define PCI_DEVICE_ID_REDHAT_BRIDGE      0x0001
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
new file mode 100644
index 0000000..9ad5802
--- /dev/null
+++ b/include/hw/virtio/virtio-gpu.h
@@ -0,0 +1,91 @@
+#ifndef _QEMU_VIRTIO_VGA_H
+#define _QEMU_VIRTIO_VGA_H
+
+#include "qemu/queue.h"
+#include "ui/qemu-pixman.h"
+#include "ui/console.h"
+#include "hw/virtio/virtio.h"
+#include "hw/pci/pci.h"
+
+#define TYPE_VIRTIO_GPU "virtio-gpu-device"
+#define VIRTIO_GPU(obj)                                        \
+        OBJECT_CHECK(VirtIOGPU, (obj), TYPE_VIRTIO_GPU)
+
+#define VIRTIO_ID_GPU 16
+
+#define VIRTGPU_MAX_RES 16
+
+#define VIRTGPU_MAX_SCANOUT 4
+
+struct virtgpu_simple_resource {
+    uint32_t resource_id;
+    uint32_t width;
+    uint32_t height;
+    uint32_t format;
+    struct iovec *iov;
+    unsigned int iov_cnt;
+    uint32_t scanout_bitmask;
+    pixman_image_t *image;
+    QLIST_ENTRY(virtgpu_simple_resource) next;
+};
+
+struct virtgpu_scanout {
+    DisplaySurface *ds;
+    uint32_t width, height;
+    int x, y;
+    int invalidate;
+    uint32_t resource_id;
+};
+
+struct virtgpu_requested_state {
+    uint32_t width, height;
+    int x, y;
+};
+
+struct virtio_gpu_conf {
+    uint32_t max_outputs;
+};
+
+typedef struct VirtIOGPU {
+    VirtIODevice parent_obj;
+
+    /* qemu console for this GPU */
+    QemuConsole *con[VIRTGPU_MAX_SCANOUT];
+
+    QEMUBH *ctrl_bh;
+    QEMUBH *cursor_bh;
+    VirtQueue *ctrl_vq;
+    VirtQueue *cursor_vq;
+    VirtQueue *event_vq;
+
+    int enable;
+
+    uint32_t current_cursor_id;
+    uint32_t cursor_generation_id;
+
+    int config_size;
+    DeviceState *qdev;
+
+    QLIST_HEAD(, virtgpu_simple_resource) reslist;
+
+    struct virtgpu_scanout scanout[VIRTGPU_MAX_SCANOUT];
+    struct virtgpu_requested_state req_state[VIRTGPU_MAX_SCANOUT];
+    QEMUCursor *current_cursor;
+
+    struct virtio_gpu_conf conf;
+    int enabled_output_bitmask;
+} VirtIOGPU;
+
+extern const GraphicHwOps virtio_gpu_ops;
+
+/* to share between PCI and VGA */
+#define DEFINE_VIRTIO_GPU_PCI_PROPERTIES(_state)                    \
+    DEFINE_PROP_BIT("ioeventfd", _state, flags, \
+                    VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), \
+    DEFINE_PROP_UINT32("vectors", _state, nvectors, 4)
+
+#define DEFINE_VIRTIO_GPU_PROPERTIES(_state, _conf_field)        \
+    DEFINE_PROP_UINT32("max_outputs", _state, _conf_field.max_outputs, 2)
+
+#endif
+
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 4/4] virtio-vga: v1
  2014-03-12 12:55 [Qemu-devel] [PATCH 0/4] vga: new display devices Gerd Hoffmann
                   ` (2 preceding siblings ...)
  2014-03-12 12:55 ` [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code Gerd Hoffmann
@ 2014-03-12 12:55 ` Gerd Hoffmann
  2014-03-12 13:55 ` [Qemu-devel] [PATCH 0/4] vga: new display devices Laszlo Ersek
  4 siblings, 0 replies; 23+ messages in thread
From: Gerd Hoffmann @ 2014-03-12 12:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, Gerd Hoffmann, Anthony Liguori, Michael S. Tsirkin

From: Dave Airlie <airlied@redhat.com>

This is a virtio-vga device built on top of the virtio-gpu device.

Signed-off-by: Dave Airlie <airlied@redhat.com>

changes by kraxel:
 * adapt to changes in master.
 * codestyle cleanups.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 Makefile                           |   2 +-
 default-configs/x86_64-softmmu.mak |   1 +
 hw/display/Makefile.objs           |   1 +
 hw/display/virtio-vga.c            | 169 +++++++++++++++++++++++++++++++++++++
 hw/pci/pci.c                       |   2 +
 include/sysemu/sysemu.h            |   2 +-
 pc-bios/vgabios-virtio.bin         | Bin 0 -> 40448 bytes
 roms/Makefile                      |   2 +-
 roms/config.vga.virtio             |   6 ++
 vl.c                               |  13 +++
 10 files changed, 195 insertions(+), 3 deletions(-)
 create mode 100644 hw/display/virtio-vga.c
 create mode 100644 pc-bios/vgabios-virtio.bin
 create mode 100644 roms/config.vga.virtio

diff --git a/Makefile b/Makefile
index bd9cd4f..d971e32 100644
--- a/Makefile
+++ b/Makefile
@@ -318,7 +318,7 @@ bepo    cz
 
 ifdef INSTALL_BLOBS
 BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
-vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \
+vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
 acpi-dsdt.aml q35-acpi-dsdt.aml \
 ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
 pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 1a00b78..22d8587 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -10,6 +10,7 @@ CONFIG_VGA_ISA=y
 CONFIG_VGA_CIRRUS=y
 CONFIG_VMWARE_VGA=y
 CONFIG_VIRTIO_GPU=y
+CONFIG_VIRTIO_VGA=y
 CONFIG_VMMOUSE=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index 6574830..6d5b792 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -36,3 +36,4 @@ common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
 
 obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu.o
 obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu-pci.o
+obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
new file mode 100644
index 0000000..ab6f899
--- /dev/null
+++ b/hw/display/virtio-vga.c
@@ -0,0 +1,169 @@
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "ui/console.h"
+#include "vga_int.h"
+#include "hw/virtio/virtio-pci.h"
+
+/*
+ * virtio-vga: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_VGA "virtio-vga"
+#define VIRTIO_VGA(obj) \
+        OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA)
+
+typedef struct VirtIOVGA {
+    VirtIOPCIProxy parent_obj;
+    VirtIOGPU      vdev;
+    VGACommonState vga;
+} VirtIOVGA;
+
+static void virtio_vga_invalidate_display(void *opaque)
+{
+    VirtIOVGA *vvga = opaque;
+
+    if (vvga->vdev.enable) {
+        virtio_gpu_ops.invalidate(&vvga->vdev);
+    } else {
+        vvga->vga.hw_ops->invalidate(&vvga->vga);
+    }
+}
+
+static void virtio_vga_update_display(void *opaque)
+{
+    VirtIOVGA *vvga = opaque;
+
+    if (vvga->vdev.enable) {
+        virtio_gpu_ops.gfx_update(&vvga->vdev);
+    } else {
+        vvga->vga.hw_ops->gfx_update(&vvga->vga);
+    }
+}
+
+static void virtio_vga_text_update(void *opaque, console_ch_t *chardata)
+{
+    VirtIOVGA *vvga = opaque;
+
+    if (vvga->vdev.enable) {
+        if (virtio_gpu_ops.text_update) {
+            virtio_gpu_ops.text_update(&vvga->vdev, chardata);
+        }
+    } else {
+        if (vvga->vga.hw_ops->text_update) {
+            vvga->vga.hw_ops->text_update(&vvga->vga, chardata);
+        }
+    }
+}
+
+static int virtio_vga_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
+{
+    VirtIOVGA *vvga = opaque;
+
+    if (vvga->vdev.enable) {
+        if (virtio_gpu_ops.ui_info) {
+            return virtio_gpu_ops.ui_info(&vvga->vdev, idx, info);
+        }
+    } else {
+        if (vvga->vga.hw_ops->ui_info) {
+            return vvga->vga.hw_ops->ui_info(&vvga->vga, idx, info);
+        }
+    }
+    return -1;
+}
+
+static const GraphicHwOps virtio_vga_ops = {
+    .invalidate = virtio_vga_invalidate_display,
+    .gfx_update = virtio_vga_update_display,
+    .text_update = virtio_vga_text_update,
+    .ui_info = virtio_vga_ui_info,
+};
+
+/* VGA device wrapper around PCI device around virtio GPU */
+static int virtio_vga_init(VirtIOPCIProxy *vpci_dev)
+{
+    VirtIOVGA *vvga = VIRTIO_VGA(vpci_dev);
+    VirtIOGPU *g = &vvga->vdev;
+    VGACommonState *vga = &vvga->vga;
+    int i;
+
+    qdev_set_parent_bus(DEVICE(g), BUS(&vpci_dev->bus));
+    if (qdev_init(DEVICE(g)) < 0) {
+        return -1;
+    }
+
+    vga_common_init(vga, OBJECT(vpci_dev), false);
+    vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev),
+             pci_address_space_io(&vpci_dev->pci_dev), true);
+
+    for (i = 0; i < g->conf.max_outputs; i++) {
+        if (i == 0) {
+            g->con[i] = graphic_console_init(DEVICE(g), i, &virtio_vga_ops,
+                                             vvga);
+            vga->con = g->con[i];
+        } else {
+            g->con[i] = graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g);
+            dpy_gfx_replace_surface(g->con[i], NULL);
+        }
+    }
+
+    pci_register_bar(&vpci_dev->pci_dev, 2,
+                     PCI_BASE_ADDRESS_MEM_PREFETCH, &vga->vram);
+
+    return 0;
+}
+
+static void virtio_vga_reset(DeviceState *dev)
+{
+    VirtIOVGA *vvga = VIRTIO_VGA(dev);
+    vvga->vdev.enable = 0;
+
+    vga_dirty_log_start(&vvga->vga);
+}
+
+static Property virtio_vga_properties[] = {
+    DEFINE_VIRTIO_GPU_PROPERTIES(VirtIOVGA, vdev.conf),
+    DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
+    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+    DEFINE_PROP_UINT32("vgamem_mb", VirtIOVGA, vga.vram_size_mb, 16),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_vga_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+    PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+    set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+    dc->props = virtio_vga_properties;
+    dc->reset = virtio_vga_reset;
+    dc->hotpluggable = false;
+
+    k->init = virtio_vga_init;
+    pcidev_k->romfile = "vgabios-virtio.bin";
+    pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_GPU;
+    pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+    pcidev_k->class_id = PCI_CLASS_DISPLAY_VGA;
+}
+
+static void virtio_vga_inst_initfn(Object *obj)
+{
+    VirtIOVGA *dev = VIRTIO_VGA(obj);
+    object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU);
+    object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+}
+
+static TypeInfo virtio_vga_info = {
+    .name          = TYPE_VIRTIO_VGA,
+    .parent        = TYPE_VIRTIO_PCI,
+    .instance_size = sizeof(struct VirtIOVGA),
+    .instance_init = virtio_vga_inst_initfn,
+    .class_init    = virtio_vga_class_init,
+};
+
+static void virtio_vga_register_types(void)
+{
+    type_register_static(&virtio_vga_info);
+}
+
+type_init(virtio_vga_register_types)
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 4e0701d..971b3ba 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -1647,6 +1647,8 @@ PCIDevice *pci_vga_init(PCIBus *bus)
         return pci_create_simple(bus, -1, "VGA");
     case VGA_VMWARE:
         return pci_create_simple(bus, -1, "vmware-svga");
+    case VGA_VIRTIO:
+        return pci_create_simple(bus, -1, "virtio-vga");
     case VGA_NONE:
     default: /* Other non-PCI types. Checking for unsupported types is already
                 done in vl.c. */
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index b90df9a..35358b8 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -104,7 +104,7 @@ extern int autostart;
 
 typedef enum {
     VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL,
-    VGA_TCX, VGA_CG3,
+    VGA_TCX, VGA_CG3, VGA_VIRTIO,
 } VGAInterfaceType;
 
 extern int vga_interface_type;
diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin
new file mode 100644
index 0000000000000000000000000000000000000000..2b70d272f2704e7839712a61613844321b93b465
GIT binary patch
literal 40448
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
AD*ylh

literal 0
HcmV?d00001

diff --git a/roms/Makefile b/roms/Makefile
index 2721b02..b88a3fd 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -1,5 +1,5 @@
 
-vgabios_variants := stdvga cirrus vmware qxl isavga
+vgabios_variants := stdvga cirrus vmware qxl isavga virtio
 vgabios_targets  := $(subst -isavga,,$(patsubst %,vgabios-%.bin,$(vgabios_variants)))
 pxerom_variants  := e1000 eepro100 ne2k_pci pcnet rtl8139 virtio
 pxerom_targets   := 8086100e 80861209 10500940 10222000 10ec8139 1af41000
diff --git a/roms/config.vga.virtio b/roms/config.vga.virtio
new file mode 100644
index 0000000..9a78983
--- /dev/null
+++ b/roms/config.vga.virtio
@@ -0,0 +1,6 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_BOCHS=y
+CONFIG_VGA_PCI=y
+CONFIG_OVERRIDE_PCI_ID=y
+CONFIG_VGA_VID=0x1af4
+CONFIG_VGA_DID=0x1010
diff --git a/vl.c b/vl.c
index bca5c95..e3ede89 100644
--- a/vl.c
+++ b/vl.c
@@ -243,6 +243,7 @@ static struct {
     { .driver = "isa-cirrus-vga",       .flag = &default_vga       },
     { .driver = "vmware-svga",          .flag = &default_vga       },
     { .driver = "qxl-vga",              .flag = &default_vga       },
+    { .driver = "virtio-vga",           .flag = &default_vga       },
 };
 
 static QemuOptsList qemu_rtc_opts = {
@@ -2085,6 +2086,11 @@ static bool cg3_vga_available(void)
     return object_class_by_name("cgthree");
 }
 
+static bool virtio_vga_available(void)
+{
+    return object_class_by_name("virtio-vga");
+}
+
 static void select_vgahw (const char *p)
 {
     const char *opts;
@@ -2111,6 +2117,13 @@ static void select_vgahw (const char *p)
             fprintf(stderr, "Error: VMWare SVGA not available\n");
             exit(0);
         }
+    } else if (strstart(p, "virtio", &opts)) {
+        if (virtio_vga_available()) {
+            vga_interface_type = VGA_VIRTIO;
+        } else {
+            fprintf(stderr, "Error: Virtio VGA not available\n");
+            exit(0);
+        }
     } else if (strstart(p, "xenfb", &opts)) {
         vga_interface_type = VGA_XENFB;
     } else if (strstart(p, "qxl", &opts)) {
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] vga: add secondary stdvga variant
  2014-03-12 12:55 ` [Qemu-devel] [PATCH 2/4] vga: add secondary stdvga variant Gerd Hoffmann
@ 2014-03-12 13:11   ` Eric Blake
  0 siblings, 0 replies; 23+ messages in thread
From: Eric Blake @ 2014-03-12 13:11 UTC (permalink / raw)
  To: Gerd Hoffmann, qemu-devel; +Cc: airlied

[-- Attachment #1: Type: text/plain, Size: 443 bytes --]

On 03/12/2014 06:55 AM, Gerd Hoffmann wrote:
> Add a standard vga variant which doesn't occupy any legacy
> ressources and thus can easily be used as secondary (or legacy-free)

s/ressources/resources/

> graphics adapter.  Programming must be done using the MMIO bar.
> 
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 0/4] vga: new display devices
  2014-03-12 12:55 [Qemu-devel] [PATCH 0/4] vga: new display devices Gerd Hoffmann
                   ` (3 preceding siblings ...)
  2014-03-12 12:55 ` [Qemu-devel] [PATCH 4/4] virtio-vga: v1 Gerd Hoffmann
@ 2014-03-12 13:55 ` Laszlo Ersek
  2014-03-12 15:47   ` Gerd Hoffmann
  4 siblings, 1 reply; 23+ messages in thread
From: Laszlo Ersek @ 2014-03-12 13:55 UTC (permalink / raw)
  To: Gerd Hoffmann, qemu-devel; +Cc: airlied

On 03/12/14 13:55, Gerd Hoffmann wrote:
>   Hi,
> 
> This patch series adds new display devices.
> 
> Number one is secondary-vga.  That is identical to VGA (aka -vga std),
> except that it doesn't occupy all the legacy vga stuff (ioports, memory
> window @ 0xa0000), so you can have more than one of these in the system.
> It has one pci memory bar for the framebuffer and one mmio bar for
> registers.  OVMF can drive it.  Doesn't use it as console for some
> reason, but initializes it and the linux kernel will see it as efifb.

My take is, due to the UEFI driver model, QemuVideoDxe is connected to
this secondary VGA (and so another GOP instance is created). It's then
probably up to GraphicsConsoleDxe to provide a SimpleTextOutput on top.
My guess (without looking) is that this too happens, again thanks to the
UEFI driver model.

What could be amiss is likely something in ConSplitterDxe, which accepts
input from all consoles, and mirrors output to all of them as well.
Perhaps it doesn't expect multiple GOPs.

Laszlo

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 0/4] vga: new display devices
  2014-03-12 13:55 ` [Qemu-devel] [PATCH 0/4] vga: new display devices Laszlo Ersek
@ 2014-03-12 15:47   ` Gerd Hoffmann
  2014-03-12 18:14     ` Laszlo Ersek
  0 siblings, 1 reply; 23+ messages in thread
From: Gerd Hoffmann @ 2014-03-12 15:47 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: airlied, qemu-devel


On Mi, 2014-03-12 at 14:55 +0100, Laszlo Ersek wrote:
> On 03/12/14 13:55, Gerd Hoffmann wrote:
> >   Hi,
> > 
> > This patch series adds new display devices.
> > 
> > Number one is secondary-vga.  That is identical to VGA (aka -vga std),
> > except that it doesn't occupy all the legacy vga stuff (ioports, memory
> > window @ 0xa0000), so you can have more than one of these in the system.
> > It has one pci memory bar for the framebuffer and one mmio bar for
> > registers.  OVMF can drive it.  Doesn't use it as console for some
> > reason, but initializes it and the linux kernel will see it as efifb.
> 
> My take is, due to the UEFI driver model, QemuVideoDxe is connected to
> this secondary VGA (and so another GOP instance is created). It's then
> probably up to GraphicsConsoleDxe to provide a SimpleTextOutput on top.
> My guess (without looking) is that this too happens, again thanks to the
> UEFI driver model.
> 
> What could be amiss is likely something in ConSplitterDxe, which accepts
> input from all consoles, and mirrors output to all of them as well.
> Perhaps it doesn't expect multiple GOPs.

Must be something else, I see this behavior with secondary-vga being the
only display device.  It prints a single line saying something along the
lines "display initialized", so SimpleTextOutput seems to be active.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 0/4] vga: new display devices
  2014-03-12 15:47   ` Gerd Hoffmann
@ 2014-03-12 18:14     ` Laszlo Ersek
  2014-03-13  8:22       ` Gerd Hoffmann
  0 siblings, 1 reply; 23+ messages in thread
From: Laszlo Ersek @ 2014-03-12 18:14 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: airlied, qemu-devel

On 03/12/14 16:47, Gerd Hoffmann wrote:
>
> On Mi, 2014-03-12 at 14:55 +0100, Laszlo Ersek wrote:
>> On 03/12/14 13:55, Gerd Hoffmann wrote:
>>>   Hi,
>>>
>>> This patch series adds new display devices.
>>>
>>> Number one is secondary-vga.  That is identical to VGA (aka -vga
>>> std), except that it doesn't occupy all the legacy vga stuff
>>> (ioports, memory window @ 0xa0000), so you can have more than one of
>>> these in the system. It has one pci memory bar for the framebuffer
>>> and one mmio bar for registers.  OVMF can drive it.  Doesn't use it
>>> as console for some reason, but initializes it and the linux kernel
>>> will see it as efifb.
>>
>> My take is, due to the UEFI driver model, QemuVideoDxe is connected
>> to this secondary VGA (and so another GOP instance is created). It's
>> then probably up to GraphicsConsoleDxe to provide a SimpleTextOutput
>> on top. My guess (without looking) is that this too happens, again
>> thanks to the UEFI driver model.
>>
>> What could be amiss is likely something in ConSplitterDxe, which
>> accepts input from all consoles, and mirrors output to all of them as
>> well. Perhaps it doesn't expect multiple GOPs.
>
> Must be something else, I see this behavior with secondary-vga being
> the only display device.  It prints a single line saying something
> along the lines "display initialized", so SimpleTextOutput seems to be
> active.

OK, maybe I got some further clues; see
"OvmfPkg/Library/PlatformBdsLib/BdsPlatform.c".

  PlatformBdsPolicyBehavior()
    PlatformBdsConnectConsole()
      DetectAndPreparePlatformPciDevicePaths()
        VisitAllPciInstances()
          VisitAllInstancesOfProtocol()
            VisitingAPciInstance()
              DetectAndPreparePlatformPciDevicePath()
    +---------->IS_PCI_VGA (Pci):
    |             "Found PCI VGA device"
    |             PreparePciVgaDevicePath()
    |               GetGopDevicePath()
    |               BdsLibUpdateConsoleVariable()
    |
The IS_PCI_VGA() macro in "MdePkg/Include/IndustryStandard/Pci22.h"
says:

> /**
>   Macro that checks whether device is a VGA-compatible controller.
>
>   @param  _p      Specified device.
>
>   @retval TRUE    Device is a VGA-compatible controller.
>   @retval FALSE   Device is not a VGA-compatible controller.
>
> **/
> #define IS_PCI_VGA(_p)                IS_CLASS3 (_p, PCI_CLASS_DISPLAY, PCI_CLASS_DISPLAY_VGA, PCI_IF_VGA_VGA)

Plus

> #define PCI_CLASS_DISPLAY             0x03
> #define   PCI_CLASS_DISPLAY_VGA         0x00
> #define     PCI_IF_VGA_VGA                0x00

In your patch 2/4 (secondary_class_init()):

> +    k->class_id = PCI_CLASS_DISPLAY_OTHER;

and from qemu's "include/hw/pci/pci_ids.h":

> #define PCI_CLASS_DISPLAY_VGA            0x0300
> #define PCI_CLASS_DISPLAY_OTHER          0x0380

I think this is the cause of the mismatch.

In vga_class_init(), PCI_CLASS_DISPLAY_VGA is used.

Note: build_pci_bus_end() [hw/i386/acpi-build.c] also seems to depend on
PCI_CLASS_DISPLAY_VGA. (Of course you could be wanting to avoid exactly
that branch, by setting the class to "other".)

In edk2, there's another relevant-looking (laxer) macro:

> /**
>   Macro that checks whether device is a display controller.
>
>   @param  _p      Specified device.
>
>   @retval TRUE    Device is a display controller.
>   @retval FALSE   Device is not a display controller.
>
> **/
> #define IS_PCI_DISPLAY(_p)            IS_CLASS1 (_p, PCI_CLASS_DISPLAY)

Does the following OVMF patch help?

> diff --git a/OvmfPkg/Library/PlatformBdsLib/BdsPlatform.c b/OvmfPkg/Library/PlatformBdsLib/BdsPlatform.c
> index 9f953ac..33d8c5d 100644
> --- a/OvmfPkg/Library/PlatformBdsLib/BdsPlatform.c
> +++ b/OvmfPkg/Library/PlatformBdsLib/BdsPlatform.c
> @@ -593,7 +593,7 @@ DetectAndPreparePlatformPciDevicePath (
>    //
>    // Here we decide which VGA device to enable in PCI bus
>    //
> -  if (IS_PCI_VGA (Pci)) {
> +  if (IS_PCI_DISPLAY (Pci)) {
>      //
>      // Add them to ConOut.
>      //

Thanks
Laszlo

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code.
  2014-03-12 12:55 ` [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code Gerd Hoffmann
@ 2014-03-12 20:26   ` Michael S. Tsirkin
  2014-03-13  9:08     ` Gerd Hoffmann
  2014-03-13 10:40     ` Paolo Bonzini
  0 siblings, 2 replies; 23+ messages in thread
From: Michael S. Tsirkin @ 2014-03-12 20:26 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: airlied, qemu-devel, Anthony Liguori

On Wed, Mar 12, 2014 at 01:55:13PM +0100, Gerd Hoffmann wrote:
> From: Dave Airlie <airlied@redhat.com>
> 
> This is the basic virtio-gpu which is
> 
> multi-head capable,
> ARGB cursor support,
> unaccelerated.
> 
> Some more info is in docs/specs/virtio-gpu.txt.
> 
> Signed-off-by: Dave Airlie <airlied@redhat.com>
> 
> changes by kraxel:
>  * adapt to changes in master.
>  * move from hw/virtio to hw/display.
>  * codestyle cleanups.
>  * misc minor fixes.
> 
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> 
> virtio-gpu codestyle

Still reviewing.
One thing to do is add this to the virtio spec.
Need help with that?

> ---
>  default-configs/x86_64-softmmu.mak |   1 +
>  docs/specs/virtio-gpu.txt          |  89 +++++
>  hw/display/Makefile.objs           |   3 +
>  hw/display/virtgpu_hw.h            | 149 ++++++++
>  hw/display/virtio-gpu-pci.c        |  80 +++++
>  hw/display/virtio-gpu.c            | 689 +++++++++++++++++++++++++++++++++++++
>  hw/virtio/virtio-pci.h             |  15 +
>  include/hw/pci/pci.h               |   1 +
>  include/hw/virtio/virtio-gpu.h     |  91 +++++
>  9 files changed, 1118 insertions(+)
>  create mode 100644 docs/specs/virtio-gpu.txt
>  create mode 100644 hw/display/virtgpu_hw.h
>  create mode 100644 hw/display/virtio-gpu-pci.c
>  create mode 100644 hw/display/virtio-gpu.c
>  create mode 100644 include/hw/virtio/virtio-gpu.h
> 
> diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
> index 31bddce..1a00b78 100644
> --- a/default-configs/x86_64-softmmu.mak
> +++ b/default-configs/x86_64-softmmu.mak
> @@ -9,6 +9,7 @@ CONFIG_VGA_PCI=y
>  CONFIG_VGA_ISA=y
>  CONFIG_VGA_CIRRUS=y
>  CONFIG_VMWARE_VGA=y
> +CONFIG_VIRTIO_GPU=y
>  CONFIG_VMMOUSE=y
>  CONFIG_SERIAL=y
>  CONFIG_PARALLEL=y
> diff --git a/docs/specs/virtio-gpu.txt b/docs/specs/virtio-gpu.txt
> new file mode 100644
> index 0000000..987d807
> --- /dev/null
> +++ b/docs/specs/virtio-gpu.txt
> @@ -0,0 +1,89 @@
> +Initial info on virtio-gpu:
> +
> +virtio-gpu is a virtio based graphics adapter. The initial version provides support for:
> +
> +- ARGB Hardware cursors
> +- multiple outputs
> +
> +There are no acceleration features available in the first release of this device, 3D acceleration features will be provided later via an interface to an external renderer.
> +
> +The virtio-gpu exposes 3 vqs to the guest,
> +
> +1) control vq - guest->host queue for sending commands and getting responses when required.
> +2) cursor vq - guest->host queue for sending cursor position and resource updates
> +3) event vq - host->guest queue for sending async events like display topography updates and errors (todo).
> +
> +How to use the virtio-gpu:
> +
> +The virtio-gpu is based around the concept of resources private to the host, the guest must DMA transfer into these resources. This is a design requirement in order to interface with future 3D rendering. In the unaccelerated there is no support for DMA transfers from resources, just to them.
> +
> +Resources are initially simple 2D resources, consisting of a width, height and format along with an identifier. The guest must then attach backing store to the resources in order for DMA transfers to work. This is like a GART in a real GPU.
> +
> +A typical guest user would create a 2D resource using VIRTGPU_CMD_RESOURCE_CREATE_2D, attach backing store using VIRTGPU_CMD_RESOURCE_ATTACH_BACKING, then attach the resource to a scanout using VIRTGPU_CMD_SET_SCANOUT, then use VIRTGPU_CMD_TRANSFER_SEND_2D to send updates to the resource, and finally VIRTGPU_CMD_RESOURCE_FLUSH to flush the scanout buffers to screen.
> +
> +Command queue:
> +
> +VIRTGPU_CMD_GET_DISPLAY_INFO:
> +Retrieve the current output configuration.
> +
> +This sends a response in the same queue slot. The response contains the max number of scanouts the host can support,
> +along with a list of per-scanout information. The info contains whether the
> +scanout is enabled, what its preferred x, y, width and height are and some future flags.
> +
> +VIRTGPU_CMD_RESOURCE_CREATE_2D:
> +Create a 2D resource on the host.
> +
> +This creates a 2D resource on the host with the specified width, height and format. Only a small subset of formats are support. The resource ids are generated by the guest.
> +
> +VIRTGPU_CMD_RESOURCE_UNREF:
> +Destroy a resource.
> +
> +This informs the host that a resource is no longer required by the guest.
> +
> +VIRTGPU_CMD_SET_SCANOUT:
> +Set the scanout parameters for a single output.
> +
> +This sets the scanout parameters for a single scanout. The resource_id is the
> +resource to be scanned out from, along with a rectangle specified by x, y, width and height.
> +
> +VIRTGPU_CMD_RESOURCE_FLUSH:
> +Flush a scanout resource
> +
> +This flushes a resource to screen, it takes a rectangle and a resource id,
> +and flushes any scanouts the resource is being used on.
> +
> +VIRTGPU_CMD_TRANSFER_TO_HOST_2D:
> +Transfer from guest memory to host resource.
> +
> +This takes a resource id along with an destination offset into the resource,
> +and a box to transfer from the host backing for the resource.
> +
> +VIRTGPU_CMD_RESOURCE_ATTACH_BACKING:
> +Assign backing pages to a resource.
> +
> +This assign an array of guest pages as the backing store for a resource. These
> +pages are then used for the transfer operations for that resource from that
> +point on.
> +
> +VIRTGPU_CMD_RESOURCE_DETACH_BACKING:
> +Detach backing pages from a resource.
> +
> +This detaches any backing pages from a resource, to be used in case of
> +guest swapping or object destruction.
> +
> +Cursor queue:
> +The cursor queue accepts on structure per vq entry. The cursor is just a standard 2D resource that is 64x64 sized. Transfers are used to upload the cursor contents.
> +
> +For cursor movement, the cursor_x and cursor_y fields only need to be updated.
> +
> +For a cursor update where the guest has transferred a new cursor into the resource, the hotspot, cursor id, should be updated, but the generation_id should also be increased by one, so the host knows to refresh its copy of the cursor data from the resource.
> +
> +Event queue:
> +The only current event passed is a message to denote the host wants to update the layout of the screens. It contains the same info as the response to VIRTGPU_CMD_GET_DISPLAY_INFO.
> +
> +
> +
> +
> +
> +
> +
> diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
> index 7ed76a9..6574830 100644
> --- a/hw/display/Makefile.objs
> +++ b/hw/display/Makefile.objs
> @@ -33,3 +33,6 @@ obj-$(CONFIG_CG3) += cg3.o
>  obj-$(CONFIG_VGA) += vga.o
>  
>  common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
> +
> +obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu.o
> +obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu-pci.o
> diff --git a/hw/display/virtgpu_hw.h b/hw/display/virtgpu_hw.h
> new file mode 100644
> index 0000000..0709922
> --- /dev/null
> +++ b/hw/display/virtgpu_hw.h
> @@ -0,0 +1,149 @@
> +/*
> + * virtgpu_hw.h - describe the virtgpu hw interface.
> + *
> + * this file is shared with the kernel and follow kernel coding
> + * conventions, we can write a qemu specific one once everything is stable,
> + * but it will make merging things messier.
> + */
> +#ifndef VIRTGPU_HW_H
> +#define VIRTGPU_HW_H
> +
> +#define VIRTGPU_CMD_HAS_RESP (1 << 31)
> +
> +enum virtgpu_ctrl_cmd {
> +        VIRTGPU_CMD_NOP,
> +        VIRTGPU_CMD_GET_DISPLAY_INFO = (1 | VIRTGPU_CMD_HAS_RESP),
> +        /* reserved */
> +        VIRTGPU_CMD_RESOURCE_CREATE_2D = 3,
> +        VIRTGPU_CMD_RESOURCE_UNREF = 4,
> +        VIRTGPU_CMD_SET_SCANOUT = 5,
> +        VIRTGPU_CMD_RESOURCE_FLUSH = 6,
> +        VIRTGPU_CMD_TRANSFER_TO_HOST_2D = 7,
> +        VIRTGPU_CMD_RESOURCE_ATTACH_BACKING = 8,
> +        VIRTGPU_CMD_RESOURCE_INVAL_BACKING = 9,
> +};
> +
> +enum virtgpu_ctrl_event {
> +        VIRTGPU_EVENT_NOP,
> +        VIRTGPU_EVENT_DISPLAY_CHANGE,
> +};
> +
> +/* data passed in the cursor vq */
> +struct virtgpu_hw_cursor_page {
> +        uint32_t cursor_x, cursor_y;
> +        uint32_t cursor_hot_x, cursor_hot_y;
> +        uint32_t cursor_id;
> +        uint32_t generation_id;
> +};
> +
> +struct virtgpu_resource_unref {
> +        uint32_t resource_id;
> +};
> +
> +/* create a simple 2d resource with a format */
> +struct virtgpu_resource_create_2d {
> +        uint32_t resource_id;
> +        uint32_t format;
> +        uint32_t width;
> +        uint32_t height;
> +};
> +
> +struct virtgpu_set_scanout {
> +        uint32_t scanout_id;
> +        uint32_t resource_id;
> +        uint32_t width;
> +        uint32_t height;
> +        uint32_t x;
> +        uint32_t y;
> +};
> +
> +struct virtgpu_resource_flush {
> +        uint32_t resource_id;
> +        uint32_t width;
> +        uint32_t height;
> +        uint32_t x;
> +        uint32_t y;
> +};
> +
> +/* simple transfer to_host */
> +struct virtgpu_transfer_to_host_2d {
> +        uint32_t resource_id;
> +        uint32_t offset;
> +        uint32_t width;
> +        uint32_t height;
> +        uint32_t x;
> +        uint32_t y;
> +};
> +
> +struct virtgpu_mem_entry {
> +        uint64_t addr;
> +        uint32_t length;
> +        uint32_t pad;
> +};
> +
> +struct virtgpu_resource_attach_backing {
> +        uint32_t resource_id;
> +        uint32_t nr_entries;
> +};
> +
> +struct virtgpu_resource_inval_backing {
> +        uint32_t resource_id;
> +};
> +
> +#define VIRTGPU_MAX_SCANOUTS 16
> +struct virtgpu_display_info {
> +        uint32_t num_scanouts;
> +        struct {
> +                uint32_t enabled;
> +                uint32_t width;
> +                uint32_t height;
> +                uint32_t x;
> +                uint32_t y;
> +                uint32_t flags;
> +        } pmodes[VIRTGPU_MAX_SCANOUTS];
> +};
> +
> +struct virtgpu_command {
> +        uint32_t type;
> +        uint32_t flags;
> +        uint64_t rsvd;
> +        union virtgpu_cmds {
> +                struct virtgpu_resource_create_2d resource_create_2d;
> +                struct virtgpu_resource_unref resource_unref;
> +                struct virtgpu_resource_flush resource_flush;
> +                struct virtgpu_set_scanout set_scanout;
> +                struct virtgpu_transfer_to_host_2d transfer_to_host_2d;
> +                struct virtgpu_resource_attach_backing resource_attach_backing;
> +                struct virtgpu_resource_inval_backing resource_inval_backing;
> +        } u;
> +};
> +
> +struct virtgpu_response {
> +        uint32_t type;
> +        uint32_t flags;
> +        union virtgpu_resps {
> +                struct virtgpu_display_info display_info;
> +        } u;
> +};
> +
> +struct virtgpu_event {
> +        uint32_t type;
> +        uint32_t err_code;
> +        union virtgpu_events {
> +                struct virtgpu_display_info display_info;
> +        } u;
> +};
> +
> +/* simple formats for fbcon/X use */
> +enum virtgpu_formats {
> +       VIRGL_FORMAT_B8G8R8A8_UNORM          = 1,
> +       VIRGL_FORMAT_B8G8R8X8_UNORM          = 2,
> +       VIRGL_FORMAT_A8R8G8B8_UNORM          = 3,
> +       VIRGL_FORMAT_X8R8G8B8_UNORM          = 4,
> +
> +       VIRGL_FORMAT_B5G5R5A1_UNORM          = 5,
> +
> +       VIRGL_FORMAT_R8_UNORM                = 64,
> +};
> +
> +#endif
> diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c
> new file mode 100644
> index 0000000..642b293
> --- /dev/null
> +++ b/hw/display/virtio-gpu-pci.c
> @@ -0,0 +1,80 @@
> +/*
> + * Virtio video device
> + *
> + * Copyright Red Hat
> + *
> + * Authors:
> + *  Dave Airlie
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.  See
> + * the COPYING file in the top-level directory.
> + *
> + */
> +#include "hw/pci/pci.h"
> +#include "hw/virtio/virtio.h"
> +#include "hw/virtio/virtio-bus.h"
> +#include "hw/virtio/virtio-pci.h"
> +#include "hw/virtio/virtio-gpu.h"
> +
> +static Property virtio_gpu_pci_properties[] = {
> +    DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
> +    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static int virtio_gpu_pci_init(VirtIOPCIProxy *vpci_dev)
> +{
> +    VirtIOGPUPCI *vgpu = VIRTIO_GPU_PCI(vpci_dev);
> +    DeviceState *vdev = DEVICE(&vgpu->vdev);
> +    VirtIOGPU *g = &vgpu->vdev;
> +    int i;
> +
> +    qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
> +    if (qdev_init(vdev) < 0) {
> +        return -1;
> +    }
> +
> +    for (i = 0; i < g->conf.max_outputs; i++) {
> +        g->con[i] = graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g);
> +        if (i > 0) {
> +            dpy_gfx_replace_surface(g->con[i], NULL);
> +        }
> +    }
> +    return 0;
> +}
> +
> +static void virtio_gpu_pci_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
> +    PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
> +
> +    set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
> +    dc->props = virtio_gpu_pci_properties;
> +    k->init = virtio_gpu_pci_init;
> +    pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
> +    pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_GPU;
> +    pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
> +    pcidev_k->class_id = PCI_CLASS_DISPLAY_OTHER;
> +}
> +
> +static void virtio_gpu_initfn(Object *obj)
> +{
> +    VirtIOGPUPCI *dev = VIRTIO_GPU_PCI(obj);
> +    object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU);
> +    object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
> +}
> +
> +static const TypeInfo virtio_gpu_pci_info = {
> +    .name = TYPE_VIRTIO_GPU_PCI,
> +    .parent = TYPE_VIRTIO_PCI,
> +    .instance_size = sizeof(VirtIOGPUPCI),
> +    .instance_init = virtio_gpu_initfn,
> +    .class_init = virtio_gpu_pci_class_init,
> +};
> +
> +static void virtio_gpu_pci_register_types(void)
> +{
> +    type_register_static(&virtio_gpu_pci_info);
> +}
> +type_init(virtio_gpu_pci_register_types)
> diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
> new file mode 100644
> index 0000000..cf7c0a2
> --- /dev/null
> +++ b/hw/display/virtio-gpu.c
> @@ -0,0 +1,689 @@
> +/*
> + * Virtio video device
> + *
> + * Copyright Red Hat
> + *
> + * Authors:
> + *  Dave Airlie
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.  See
> + * the COPYING file in the top-level directory.
> + *
> + */
> +
> +#include "qemu-common.h"
> +#include "qemu/iov.h"
> +#include "ui/console.h"
> +#include "hw/virtio/virtio.h"
> +#include "hw/virtio/virtio-gpu.h"
> +#include "hw/virtio/virtio-bus.h"
> +
> +#include "virtgpu_hw.h"
> +
> +static struct virtgpu_simple_resource *virtgpu_find_resource(VirtIOGPU *g,
> +                                                          uint32_t resource_id);
> +
> +static void update_cursor_data(VirtIOGPU *g,
> +                               struct virtgpu_simple_resource *res)
> +{
> +    uint32_t pixels;
> +    int i;
> +
> +    pixels = g->current_cursor->width * g->current_cursor->height;
> +    for (i = 0; i < pixels; i++) {
> +        uint32_t value = ((uint32_t *)pixman_image_get_data(res->image))[i];
> +        g->current_cursor->data[i] = value;
> +    }
> +}
> +
> +static void update_cursor(VirtIOGPU *g, struct virtgpu_hw_cursor_page *cursor)
> +{
> +    if (g->cursor_generation_id != cursor->generation_id) {
> +
> +        if (!g->current_cursor) {
> +            g->current_cursor = cursor_alloc(64, 64);
> +        }
> +
> +        g->current_cursor->hot_x = cursor->cursor_hot_x;
> +        g->current_cursor->hot_y = cursor->cursor_hot_y;
> +
> +        if (cursor->cursor_id > 0) {
> +            struct virtgpu_simple_resource *res;
> +
> +            res = virtgpu_find_resource(g, cursor->cursor_id);
> +            if (res) {
> +                update_cursor_data(g, res);
> +            }
> +        }
> +        g->cursor_generation_id = cursor->generation_id;
> +        g->current_cursor_id = cursor->cursor_id;
> +        dpy_cursor_define(g->con[0], g->current_cursor);
> +    }
> +
> +    dpy_mouse_set(g->con[0], cursor->cursor_x, cursor->cursor_y,
> +                  cursor->cursor_id ? 1 : 0);
> +}
> +
> +static void virtio_gpu_get_config(VirtIODevice *vdev, uint8_t *config)
> +{
> +}
> +
> +static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config)
> +{
> +}
> +
> +static uint32_t virtio_gpu_get_features(VirtIODevice *vdev, uint32_t features)
> +{
> +    return features;
> +}
> +
> +static void virtio_gpu_set_features(VirtIODevice *vdev, uint32_t features)
> +{
> +}
> +
> +static struct virtgpu_simple_resource *virtgpu_find_resource(VirtIOGPU *g,
> +                                                          uint32_t resource_id)
> +{
> +    struct virtgpu_simple_resource *res;
> +
> +    QLIST_FOREACH(res, &g->reslist, next) {
> +        if (res->resource_id == resource_id) {
> +            return res;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +static void virtgpu_fill_display_info(VirtIOGPU *g,
> +                                      struct virtgpu_display_info *dpy_info)
> +{
> +    int i;
> +
> +    memset(dpy_info, 0, sizeof(*dpy_info));
> +    dpy_info->num_scanouts = g->conf.max_outputs;
> +    for (i = 0; i < g->conf.max_outputs; i++) {
> +        if (g->enabled_output_bitmask & (1 << i)) {
> +            dpy_info->pmodes[i].enabled = 1;
> +            dpy_info->pmodes[i].width = g->req_state[i].width;
> +            dpy_info->pmodes[i].height = g->req_state[i].height;
> +        }
> +    }
> +}
> +
> +static void virtgpu_get_display_info(VirtIOGPU *g,
> +                                     struct iovec *iov,
> +                                     unsigned int iov_cnt)
> +{
> +    struct virtgpu_response resp;
> +
> +    resp.type = VIRTGPU_CMD_GET_DISPLAY_INFO;
> +    resp.flags = 0;
> +    virtgpu_fill_display_info(g, &resp.u.display_info);
> +
> +    iov_from_buf(iov, iov_cnt, 0, &resp, sizeof(struct virtgpu_response));
> +}
> +
> +static pixman_format_code_t get_pixman_format(uint32_t virtgpu_format)
> +{
> +    switch (virtgpu_format) {
> +    case VIRGL_FORMAT_B8G8R8X8_UNORM:
> +        return PIXMAN_x8r8g8b8;
> +    case VIRGL_FORMAT_B8G8R8A8_UNORM:
> +        return PIXMAN_a8r8g8b8;
> +    default:
> +        assert(0);
> +        break;
> +    }
> +    return 0;
> +}
> +
> +static void virtgpu_resource_create_2d(VirtIOGPU *g,
> +                                       struct virtgpu_resource_create_2d *c2d)
> +{
> +    pixman_format_code_t pformat;
> +    struct virtgpu_simple_resource *res;
> +
> +    res = g_new0(struct virtgpu_simple_resource, 1);
> +    if (!res) {
> +        return;
> +    }
> +
> +    res->width = c2d->width;
> +    res->height = c2d->height;
> +    res->format = c2d->format;
> +    res->resource_id = c2d->resource_id;
> +
> +    pformat = get_pixman_format(c2d->format);
> +    res->image = pixman_image_create_bits(pformat,
> +                                          c2d->width,
> +                                          c2d->height,
> +                                          NULL, 0);
> +
> +    QLIST_INSERT_HEAD(&g->reslist, res, next);
> +}
> +
> +static void virtgpu_resource_destroy(struct virtgpu_simple_resource *res)
> +{
> +    pixman_image_unref(res->image);
> +    QLIST_REMOVE(res, next);
> +    g_free(res);
> +}
> +
> +static void virtgpu_resource_unref(VirtIOGPU *g,
> +                                   uint32_t resource_id)
> +{
> +    struct virtgpu_simple_resource *res = virtgpu_find_resource(g, resource_id);
> +
> +    if (!res) {
> +        return;
> +    }
> +    virtgpu_resource_destroy(res);
> +}
> +
> +static void virtgpu_transfer_to_host_2d(VirtIOGPU *g,
> +                                     struct virtgpu_transfer_to_host_2d *t2d)
> +{
> +    struct virtgpu_simple_resource *res =
> +        virtgpu_find_resource(g, t2d->resource_id);
> +    int h;
> +    uint32_t src_offset, dst_offset, stride;
> +    int bpp;
> +    pixman_format_code_t format;
> +
> +    if (!res || !res->iov) {
> +        return;
> +    }
> +
> +    format = pixman_image_get_format(res->image);
> +    bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
> +    stride = pixman_image_get_stride(res->image);
> +
> +    if (t2d->offset || t2d->x || t2d->y ||
> +        t2d->width != pixman_image_get_width(res->image)) {
> +        for (h = 0; h < t2d->height; h++) {
> +            src_offset = t2d->offset + stride * h;
> +            dst_offset = (t2d->y + h) * stride + (t2d->x * bpp);
> +
> +            iov_to_buf(res->iov, res->iov_cnt, src_offset,
> +                       (uint8_t *)pixman_image_get_data(res->image)
> +                       + dst_offset, t2d->width * bpp);
> +        }
> +    } else {
> +        iov_to_buf(res->iov, res->iov_cnt, 0,
> +                   pixman_image_get_data(res->image),
> +                   pixman_image_get_stride(res->image)
> +                   * pixman_image_get_height(res->image));
> +    }
> +}
> +
> +static void virtgpu_resource_flush(VirtIOGPU *g,
> +                                   struct virtgpu_resource_flush *rf)
> +{
> +    struct virtgpu_simple_resource *res =
> +        virtgpu_find_resource(g, rf->resource_id);
> +    pixman_region16_t flush_region;
> +    int i;
> +
> +    pixman_region_init_rect(&flush_region,
> +                            rf->x, rf->y, rf->width, rf->height);
> +    for (i = 0; i < VIRTGPU_MAX_SCANOUT; i++) {
> +        struct virtgpu_scanout *scanout;
> +        pixman_region16_t region, finalregion;
> +        pixman_box16_t *extents;
> +
> +        if (!(res->scanout_bitmask & (1 << i))) {
> +            continue;
> +        }
> +        scanout = &g->scanout[i];
> +
> +        pixman_region_init(&finalregion);
> +        pixman_region_init_rect(&region, scanout->x, scanout->y,
> +                                scanout->width, scanout->height);
> +
> +        pixman_region_intersect(&finalregion, &flush_region, &region);
> +        pixman_region_translate(&finalregion, -scanout->x, -scanout->y);
> +        extents = pixman_region_extents(&finalregion);
> +        /* work out the area we need to update for each console */
> +        dpy_gfx_update(g->con[i], extents->x1, extents->y1,
> +                       extents->x2 - extents->x1,
> +                       extents->y2 - extents->y1);
> +
> +        pixman_region_fini(&region);
> +        pixman_region_fini(&finalregion);
> +    }
> +    pixman_region_fini(&flush_region);
> +}
> +
> +static void virtgpu_set_scanout(VirtIOGPU *g,
> +                                struct virtgpu_set_scanout *ss)
> +{
> +    struct virtgpu_simple_resource *res =
> +        virtgpu_find_resource(g, ss->resource_id);
> +    struct virtgpu_scanout *scanout;
> +    pixman_format_code_t format;
> +    uint32_t offset;
> +    int bpp;
> +
> +    g->enable = 1;
> +    if (ss->resource_id == 0) {
> +        scanout = &g->scanout[ss->scanout_id];
> +        if (g->scanout[ss->scanout_id].resource_id) {
> +            res = virtgpu_find_resource(g, scanout->resource_id);
> +            if (res) {
> +                res->scanout_bitmask &= ~(1 << ss->scanout_id);
> +            }
> +        }
> +        if (ss->scanout_id == 0) {
> +            return;
> +        }
> +        dpy_gfx_replace_surface(g->con[ss->scanout_id], NULL);
> +        scanout->ds = NULL;
> +        scanout->width = 0;
> +        scanout->height = 0;
> +
> +#if 0
> +        dpy_notify_state(g->con[ss->scanout_id], 0, 0, 0, 0);
> +#endif
> +        return;
> +    }
> +    /* create a surface for this scanout */
> +
> +    if (ss->scanout_id >= VIRTGPU_MAX_SCANOUT) {
> +        fprintf(stderr, "set scanout for non-0 surface %d\n", ss->scanout_id);
> +        return;
> +    }
> +    scanout = &g->scanout[ss->scanout_id];
> +
> +    format = pixman_image_get_format(res->image);
> +    bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
> +    offset = (ss->x * bpp) + ss->y * pixman_image_get_stride(res->image);
> +
> +#if 0
> +    dpy_notify_state(g->con[ss->scanout_id], ss->x, ss->y,
> +                     ss->width, ss->height);
> +#endif
> +    if (!scanout->ds || surface_data(scanout->ds)
> +        != ((uint8_t *)pixman_image_get_data(res->image) + offset) ||
> +        scanout->width != ss->width ||
> +        scanout->height != ss->height) {
> +        /* realloc the surface ptr */
> +        scanout->ds = qemu_create_displaysurface_from
> +            (ss->width, ss->height, 32,
> +             pixman_image_get_stride(res->image),
> +             (uint8_t *)pixman_image_get_data(res->image) + offset, FALSE);
> +        if (!scanout->ds) {
> +            return;
> +        }
> +        dpy_gfx_replace_surface(g->con[ss->scanout_id], scanout->ds);
> +    }
> +
> +    res->scanout_bitmask |= (1 << ss->scanout_id);
> +    scanout->resource_id = ss->resource_id;
> +    scanout->x = ss->x;
> +    scanout->y = ss->y;
> +    scanout->width = ss->width;
> +    scanout->height = ss->height;
> +}
> +
> +static void
> +virtgpu_resource_attach_backing(VirtIOGPU *g,
> +                                struct virtgpu_resource_attach_backing *att_rb,
> +                                struct iovec *iov,
> +                                unsigned int iov_cnt)
> +{
> +    struct virtgpu_simple_resource *res =
> +        virtgpu_find_resource(g, att_rb->resource_id);
> +    uint32_t gsize = iov_size(iov, iov_cnt);
> +    struct iovec *res_iovs;
> +    int i;
> +    void *data;
> +
> +    if (!res) {
> +        return;
> +    }
> +
> +#if 0
> +    res_iovs = g_new0(struct iovec, att_rb->nr_entries);
> +#else
> +    res_iovs = g_malloc0(sizeof(struct iovec) * att_rb->nr_entries);
> +#endif
> +
> +    if (iov_cnt > 1) {
> +        data = g_malloc(gsize);
> +        iov_to_buf(iov, iov_cnt, 0, data, gsize);
> +    } else {
> +        data = iov[0].iov_base;
> +    }
> +
> +    for (i = 0; i < att_rb->nr_entries; i++) {
> +        struct virtgpu_mem_entry *ent = ((struct virtgpu_mem_entry *)data) + i;
> +        hwaddr len;
> +        res_iovs[i].iov_len = ent->length;
> +
> +        len = ent->length;
> +        res_iovs[i].iov_base = cpu_physical_memory_map(ent->addr, &len, 1);
> +        if (!res_iovs[i].iov_base || len != ent->length) {
> +            fprintf(stderr, "virtgp: trying to map MMIO memory");
> +            exit(1);
> +        }
> +    }
> +
> +    res->iov = res_iovs;
> +    res->iov_cnt = att_rb->nr_entries;
> +
> +    if (iov_cnt > 1) {
> +        g_free(data);
> +    }
> +}
> +
> +static void virtgpu_resource_inval_backing(VirtIOGPU *g,
> +                                           uint32_t resource_id)
> +{
> +    struct virtgpu_simple_resource *res
> +        = virtgpu_find_resource(g, resource_id);
> +    int i;
> +
> +    if (!res || !res->iov) {
> +        return;
> +    }
> +
> +    for (i = 0; i < res->iov_cnt; i++) {
> +        cpu_physical_memory_unmap(res->iov[i].iov_base, res->iov[i].iov_len, 1,
> +                                  res->iov[i].iov_len);
> +    }
> +    g_free(res->iov);
> +    res->iov_cnt = 0;
> +    res->iov = NULL;
> +}
> +
> +static void virtio_gpu_process_cmd(VirtIOGPU *g,
> +                                   struct virtgpu_command *cmd,
> +                                   struct iovec *iov,
> +                                   unsigned int iov_cnt)
> +{
> +    switch (cmd->type) {
> +    case VIRTGPU_CMD_GET_DISPLAY_INFO:
> +        if (iov_cnt < 1) {
> +            return;
> +        }
> +        virtgpu_get_display_info(g, iov, iov_cnt);
> +        break;
> +    case VIRTGPU_CMD_RESOURCE_CREATE_2D:
> +        virtgpu_resource_create_2d(g, &cmd->u.resource_create_2d);
> +        break;
> +    case VIRTGPU_CMD_RESOURCE_UNREF:
> +        virtgpu_resource_unref(g, cmd->u.resource_unref.resource_id);
> +        break;
> +    case VIRTGPU_CMD_RESOURCE_FLUSH:
> +        virtgpu_resource_flush(g, &cmd->u.resource_flush);
> +        break;
> +    case VIRTGPU_CMD_TRANSFER_TO_HOST_2D:
> +        virtgpu_transfer_to_host_2d(g, &cmd->u.transfer_to_host_2d);
> +        break;
> +    case VIRTGPU_CMD_SET_SCANOUT:
> +        virtgpu_set_scanout(g, &cmd->u.set_scanout);
> +        break;
> +    case VIRTGPU_CMD_RESOURCE_ATTACH_BACKING:
> +        virtgpu_resource_attach_backing(g, &cmd->u.resource_attach_backing,
> +                                        iov, iov_cnt);
> +        break;
> +    case VIRTGPU_CMD_RESOURCE_INVAL_BACKING:
> +        virtgpu_resource_inval_backing
> +            (g, cmd->u.resource_inval_backing.resource_id);
> +        break;
> +    case VIRTGPU_CMD_NOP:
> +        break;
> +    default:
> +        fprintf(stderr, "got bad command from host %d\n", cmd->type);
> +        break;
> +    }
> +}
> +
> +static void virtio_gpu_handle_ctrl_cb(VirtIODevice *vdev, VirtQueue *vq)
> +{
> +    VirtIOGPU *g = VIRTIO_GPU(vdev);
> +    qemu_bh_schedule(g->ctrl_bh);
> +}
> +
> +static void virtio_gpu_handle_cursor_cb(VirtIODevice *vdev, VirtQueue *vq)
> +{
> +    VirtIOGPU *g = VIRTIO_GPU(vdev);
> +    qemu_bh_schedule(g->cursor_bh);
> +}
> +
> +static void virtio_gpu_handle_event_cb(VirtIODevice *vdev, VirtQueue *vq)
> +{
> +}
> +
> +
> +static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
> +{
> +    VirtIOGPU *g = VIRTIO_GPU(vdev);
> +    struct iovec *iov;
> +    VirtQueueElement elem;
> +    struct virtgpu_command cmd;
> +    size_t s;
> +    unsigned int iov_cnt;
> +
> +    if (!virtio_queue_ready(vq)) {
> +        return;
> +    }
> +    while (virtqueue_pop(vq, &elem)) {
> +
> +        iov = elem.out_sg;
> +        iov_cnt = elem.out_num;
> +
> +        s = iov_to_buf(iov, iov_cnt, 0, &cmd, sizeof(cmd));
> +        if (s != sizeof(cmd)) {
> +            fprintf(stderr, "error\n");
> +        } else {
> +            if (elem.in_num > 0) {
> +                virtio_gpu_process_cmd(g, &cmd, elem.in_sg, elem.in_num);
> +            } else {
> +                virtio_gpu_process_cmd(g, &cmd, &iov[1], iov_cnt - 1);
> +            }
> +        }
> +        virtqueue_push(vq, &elem, 0);
> +        virtio_notify(vdev, vq);
> +    }
> +}
> +
> +static void virtio_gpu_ctrl_bh(void *opaque)
> +{
> +    VirtIOGPU *g = opaque;
> +    virtio_gpu_handle_ctrl(&g->parent_obj, g->ctrl_vq);
> +}
> +
> +static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq)
> +{
> +    VirtIOGPU *g = VIRTIO_GPU(vdev);
> +    VirtQueueElement elem;
> +    size_t s;
> +    struct virtgpu_hw_cursor_page cursor_info;
> +
> +    if (!virtio_queue_ready(vq)) {
> +        return;
> +    }
> +    while (virtqueue_pop(vq, &elem)) {
> +        s = iov_to_buf(elem.out_sg, elem.out_num, 0,
> +                       &cursor_info, sizeof(cursor_info));
> +        if (s != sizeof(cursor_info)) {
> +            fprintf(stderr, "error\n");
> +        } else {
> +            update_cursor(g, &cursor_info);
> +        }
> +        virtqueue_push(vq, &elem, 0);
> +        virtio_notify(vdev, vq);
> +    }
> +}
> +
> +static void virtio_gpu_cursor_bh(void *opaque)
> +{
> +    VirtIOGPU *g = opaque;
> +    virtio_gpu_handle_cursor(&g->parent_obj, g->cursor_vq);
> +}
> +
> +static int virtio_gpu_send_event(VirtIOGPU *g, struct virtgpu_event *event)
> +{
> +    VirtQueueElement elem;
> +
> +    if (!virtio_queue_ready(g->event_vq)) {
> +        return -1;
> +    }
> +
> +    if (!virtqueue_pop(g->event_vq, &elem)) {
> +        return -1;
> +    }
> +
> +    iov_from_buf(elem.in_sg, elem.in_num, 0, event,
> +                 sizeof(struct virtgpu_event));
> +
> +    virtqueue_push(g->event_vq, &elem, sizeof(struct virtgpu_event));
> +    virtio_notify(&g->parent_obj, g->event_vq);
> +    return 0;
> +}
> +
> +static void virtio_gpu_invalidate_display(void *opaque)
> +{
> +}
> +
> +static void virtio_gpu_update_display(void *opaque)
> +{
> +}
> +
> +static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata)
> +{
> +}
> +
> +static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
> +{
> +    VirtIOGPU *g = opaque;
> +    struct virtgpu_event event;
> +
> +    if (idx > g->conf.max_outputs) {
> +        return -1;
> +    }
> +
> +    g->req_state[idx].x = info->xoff;
> +    g->req_state[idx].y = info->yoff;
> +    g->req_state[idx].width = info->width;
> +    g->req_state[idx].height = info->height;
> +
> +    if (info->width && info->height) {
> +        g->enabled_output_bitmask |= (1 << idx);
> +    } else {
> +        g->enabled_output_bitmask &= ~(1 << idx);
> +    }
> +
> +    /* send event to guest */
> +    event.type = VIRTGPU_EVENT_DISPLAY_CHANGE;
> +    event.err_code = 0;
> +    virtgpu_fill_display_info(g, &event.u.display_info);
> +    virtio_gpu_send_event(g, &event);
> +    return 0;
> +}
> +
> +const GraphicHwOps virtio_gpu_ops = {
> +    .invalidate = virtio_gpu_invalidate_display,
> +    .gfx_update = virtio_gpu_update_display,
> +    .text_update = virtio_gpu_text_update,
> +    .ui_info = virtio_gpu_ui_info,
> +};
> +
> +static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
> +{
> +    VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
> +    VirtIOGPU *g = VIRTIO_GPU(qdev);
> +
> +    g->cursor_generation_id = 0xffffffff;
> +    g->config_size = 0;
> +    virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU,
> +                g->config_size);
> +
> +    g->req_state[0].width = 1024;
> +    g->req_state[0].height = 768;
> +
> +    g->ctrl_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb);
> +    g->cursor_vq = virtio_add_queue(vdev, 256, virtio_gpu_handle_cursor_cb);
> +
> +    g->ctrl_bh = qemu_bh_new(virtio_gpu_ctrl_bh, g);
> +    g->cursor_bh = qemu_bh_new(virtio_gpu_cursor_bh, g);
> +
> +    g->event_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_event_cb);
> +
> +    g->enabled_output_bitmask = 1;
> +    g->qdev = qdev;
> +}
> +
> +static void virtio_gpu_instance_init(Object *obj)
> +{
> +}
> +
> +static void virtio_gpu_reset(VirtIODevice *vdev)
> +{
> +    VirtIOGPU *g = VIRTIO_GPU(vdev);
> +    struct virtgpu_simple_resource *res, *tmp;
> +    int i;
> +
> +    g->cursor_generation_id = 0xffffffff;
> +    g->current_cursor_id = 0;
> +    g->enable = 0;
> +
> +    QLIST_FOREACH_SAFE(res, &g->reslist, next, tmp) {
> +        virtgpu_resource_destroy(res);
> +    }
> +    for (i = 0; i < g->conf.max_outputs; i++) {
> +        g->req_state[i].x = 0;
> +        g->req_state[i].y = 0;
> +        if (i == 0) {
> +            g->req_state[0].width = 1024;
> +            g->req_state[0].height = 768;
> +        } else {
> +            g->req_state[i].width = 0;
> +            g->req_state[i].height = 0;
> +        }
> +        g->scanout[i].resource_id = 0;
> +        g->scanout[i].width = 0;
> +        g->scanout[i].height = 0;
> +        g->scanout[i].x = 0;
> +        g->scanout[i].y = 0;
> +        g->scanout[i].ds = NULL;
> +    }
> +    g->enabled_output_bitmask = 1;
> +}
> +
> +static Property virtio_gpu_properties[] = {
> +    DEFINE_VIRTIO_GPU_PROPERTIES(VirtIOGPU, conf),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void virtio_gpu_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
> +
> +    vdc->realize = virtio_gpu_device_realize;
> +    vdc->get_config = virtio_gpu_get_config;
> +    vdc->set_config = virtio_gpu_set_config;
> +    vdc->get_features = virtio_gpu_get_features;
> +    vdc->set_features = virtio_gpu_set_features;
> +
> +    vdc->reset = virtio_gpu_reset;
> +
> +    dc->props = virtio_gpu_properties;
> +}
> +
> +static const TypeInfo virtio_gpu_info = {
> +    .name = TYPE_VIRTIO_GPU,
> +    .parent = TYPE_VIRTIO_DEVICE,
> +    .instance_size = sizeof(VirtIOGPU),
> +    .instance_init = virtio_gpu_instance_init,
> +    .class_init = virtio_gpu_class_init,
> +};
> +
> +static void virtio_register_types(void)
> +{
> +    type_register_static(&virtio_gpu_info);
> +}
> +
> +type_init(virtio_register_types)
> diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
> index dc332ae..fecb801 100644
> --- a/hw/virtio/virtio-pci.h
> +++ b/hw/virtio/virtio-pci.h
> @@ -24,6 +24,7 @@
>  #include "hw/virtio/virtio-balloon.h"
>  #include "hw/virtio/virtio-bus.h"
>  #include "hw/virtio/virtio-9p.h"
> +#include "hw/virtio/virtio-gpu.h"
>  #ifdef CONFIG_VIRTFS
>  #include "hw/9pfs/virtio-9p.h"
>  #endif
> @@ -39,6 +40,7 @@ typedef struct VirtIOSerialPCI VirtIOSerialPCI;
>  typedef struct VirtIONetPCI VirtIONetPCI;
>  typedef struct VHostSCSIPCI VHostSCSIPCI;
>  typedef struct VirtIORngPCI VirtIORngPCI;
> +typedef struct VirtIOGPUPCI VirtIOGPUPCI;
>  
>  /* virtio-pci-bus */
>  
> @@ -199,6 +201,19 @@ struct VirtIORngPCI {
>      VirtIORNG vdev;
>  };
>  
> +/*
> + * virtio-gpu-pci: This extends VirtioPCIProxy.
> + */
> +#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci"
> +#define VIRTIO_GPU_PCI(obj) \
> +        OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI)
> +
> +struct VirtIOGPUPCI {
> +    VirtIOPCIProxy parent_obj;
> +    VirtIOGPU vdev;
> +};
> +
> +
>  /* Virtio ABI version, if we increment this, we break the guest driver. */
>  #define VIRTIO_PCI_ABI_VERSION          0
>  
> diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
> index 693dd6b..7fc96d2 100644
> --- a/include/hw/pci/pci.h
> +++ b/include/hw/pci/pci.h
> @@ -80,6 +80,7 @@
>  #define PCI_DEVICE_ID_VIRTIO_SCSI        0x1004
>  #define PCI_DEVICE_ID_VIRTIO_RNG         0x1005
>  #define PCI_DEVICE_ID_VIRTIO_9P          0x1009
> +#define PCI_DEVICE_ID_VIRTIO_GPU         0x1010
>  
>  #define PCI_VENDOR_ID_REDHAT             0x1b36
>  #define PCI_DEVICE_ID_REDHAT_BRIDGE      0x0001
> diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
> new file mode 100644
> index 0000000..9ad5802
> --- /dev/null
> +++ b/include/hw/virtio/virtio-gpu.h
> @@ -0,0 +1,91 @@
> +#ifndef _QEMU_VIRTIO_VGA_H
> +#define _QEMU_VIRTIO_VGA_H
> +
> +#include "qemu/queue.h"
> +#include "ui/qemu-pixman.h"
> +#include "ui/console.h"
> +#include "hw/virtio/virtio.h"
> +#include "hw/pci/pci.h"
> +
> +#define TYPE_VIRTIO_GPU "virtio-gpu-device"
> +#define VIRTIO_GPU(obj)                                        \
> +        OBJECT_CHECK(VirtIOGPU, (obj), TYPE_VIRTIO_GPU)
> +
> +#define VIRTIO_ID_GPU 16
> +
> +#define VIRTGPU_MAX_RES 16
> +
> +#define VIRTGPU_MAX_SCANOUT 4
> +
> +struct virtgpu_simple_resource {
> +    uint32_t resource_id;
> +    uint32_t width;
> +    uint32_t height;
> +    uint32_t format;
> +    struct iovec *iov;
> +    unsigned int iov_cnt;
> +    uint32_t scanout_bitmask;
> +    pixman_image_t *image;
> +    QLIST_ENTRY(virtgpu_simple_resource) next;
> +};
> +
> +struct virtgpu_scanout {
> +    DisplaySurface *ds;
> +    uint32_t width, height;
> +    int x, y;
> +    int invalidate;
> +    uint32_t resource_id;
> +};
> +
> +struct virtgpu_requested_state {
> +    uint32_t width, height;
> +    int x, y;
> +};
> +
> +struct virtio_gpu_conf {
> +    uint32_t max_outputs;
> +};
> +
> +typedef struct VirtIOGPU {
> +    VirtIODevice parent_obj;
> +
> +    /* qemu console for this GPU */
> +    QemuConsole *con[VIRTGPU_MAX_SCANOUT];
> +
> +    QEMUBH *ctrl_bh;
> +    QEMUBH *cursor_bh;
> +    VirtQueue *ctrl_vq;
> +    VirtQueue *cursor_vq;
> +    VirtQueue *event_vq;
> +
> +    int enable;
> +
> +    uint32_t current_cursor_id;
> +    uint32_t cursor_generation_id;
> +
> +    int config_size;
> +    DeviceState *qdev;
> +
> +    QLIST_HEAD(, virtgpu_simple_resource) reslist;
> +
> +    struct virtgpu_scanout scanout[VIRTGPU_MAX_SCANOUT];
> +    struct virtgpu_requested_state req_state[VIRTGPU_MAX_SCANOUT];
> +    QEMUCursor *current_cursor;
> +
> +    struct virtio_gpu_conf conf;
> +    int enabled_output_bitmask;
> +} VirtIOGPU;
> +
> +extern const GraphicHwOps virtio_gpu_ops;
> +
> +/* to share between PCI and VGA */
> +#define DEFINE_VIRTIO_GPU_PCI_PROPERTIES(_state)                    \
> +    DEFINE_PROP_BIT("ioeventfd", _state, flags, \
> +                    VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), \
> +    DEFINE_PROP_UINT32("vectors", _state, nvectors, 4)
> +
> +#define DEFINE_VIRTIO_GPU_PROPERTIES(_state, _conf_field)        \
> +    DEFINE_PROP_UINT32("max_outputs", _state, _conf_field.max_outputs, 2)
> +
> +#endif
> +
> -- 
> 1.8.3.1

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 0/4] vga: new display devices
  2014-03-12 18:14     ` Laszlo Ersek
@ 2014-03-13  8:22       ` Gerd Hoffmann
  2014-03-13  8:41         ` Laszlo Ersek
  0 siblings, 1 reply; 23+ messages in thread
From: Gerd Hoffmann @ 2014-03-13  8:22 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: airlied, qemu-devel

>     +---------->IS_PCI_VGA (Pci):
>     |             "Found PCI VGA device"

> > +    k->class_id = PCI_CLASS_DISPLAY_OTHER;
> 
> and from qemu's "include/hw/pci/pci_ids.h":
> 
> > #define PCI_CLASS_DISPLAY_VGA            0x0300
> > #define PCI_CLASS_DISPLAY_OTHER          0x0380
> 
> I think this is the cause of the mismatch.

Yes, it is.  First byte (0x03) of the class says what it is, second byte
of class (0x00 / 0x80) specifies the programming interface.

So, it is a display adapter, but it isn't vga compatible, therefore
specifying PCI_CLASS_DISPLAY_VGA would be asking for trouble as trying
to program it using the standard vga io ports isn't going to work ...

[ same picture with virtio-gpu btw:  The variant with the vga compat
  bits declares itself as PCI_CLASS_DISPLAY_VGA whereas the variant
  without is PCI_CLASS_DISPLAY_OTHER ]

> Does the following OVMF patch help?
> 
> > -  if (IS_PCI_VGA (Pci)) {
> > +  if (IS_PCI_DISPLAY (Pci)) {

Yes.

thanks,
  Gerd

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 0/4] vga: new display devices
  2014-03-13  8:22       ` Gerd Hoffmann
@ 2014-03-13  8:41         ` Laszlo Ersek
  0 siblings, 0 replies; 23+ messages in thread
From: Laszlo Ersek @ 2014-03-13  8:41 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: airlied, qemu-devel

On 03/13/14 09:22, Gerd Hoffmann wrote:
>>     +---------->IS_PCI_VGA (Pci):
>>     |             "Found PCI VGA device"
> 
>>> +    k->class_id = PCI_CLASS_DISPLAY_OTHER;
>>
>> and from qemu's "include/hw/pci/pci_ids.h":
>>
>>> #define PCI_CLASS_DISPLAY_VGA            0x0300
>>> #define PCI_CLASS_DISPLAY_OTHER          0x0380
>>
>> I think this is the cause of the mismatch.
> 
> Yes, it is.  First byte (0x03) of the class says what it is, second byte
> of class (0x00 / 0x80) specifies the programming interface.
> 
> So, it is a display adapter, but it isn't vga compatible, therefore
> specifying PCI_CLASS_DISPLAY_VGA would be asking for trouble as trying
> to program it using the standard vga io ports isn't going to work ...
> 
> [ same picture with virtio-gpu btw:  The variant with the vga compat
>   bits declares itself as PCI_CLASS_DISPLAY_VGA whereas the variant
>   without is PCI_CLASS_DISPLAY_OTHER ]
> 
>> Does the following OVMF patch help?
>>
>>> -  if (IS_PCI_VGA (Pci)) {
>>> +  if (IS_PCI_DISPLAY (Pci)) {
> 
> Yes.

Please ping me when the series is merged and I will submit the patch
(possibly also correcting the related "Vga" references throughout the file).

Thanks
Laszlo

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code.
  2014-03-12 20:26   ` Michael S. Tsirkin
@ 2014-03-13  9:08     ` Gerd Hoffmann
  2014-03-14 11:13       ` Gerd Hoffmann
  2014-03-13 10:40     ` Paolo Bonzini
  1 sibling, 1 reply; 23+ messages in thread
From: Gerd Hoffmann @ 2014-03-13  9:08 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: airlied, qemu-devel, Anthony Liguori

  Hi,

> One thing to do is add this to the virtio spec.
> Need help with that?

What is the authoritative spec these days?
The virtio 1.0 draft?
Where can I find the latest revision?
Where should I send patches?

> >  docs/specs/virtio-gpu.txt          |  89 +++++

Some docs are here.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code.
  2014-03-12 20:26   ` Michael S. Tsirkin
  2014-03-13  9:08     ` Gerd Hoffmann
@ 2014-03-13 10:40     ` Paolo Bonzini
  2014-03-14 11:18       ` Gerd Hoffmann
  2014-03-17  4:36       ` Dave Airlie
  1 sibling, 2 replies; 23+ messages in thread
From: Paolo Bonzini @ 2014-03-13 10:40 UTC (permalink / raw)
  To: Michael S. Tsirkin, Gerd Hoffmann; +Cc: airlied, qemu-devel, Anthony Liguori

Il 12/03/2014 21:26, Michael S. Tsirkin ha scritto:
>> +Event queue:
>> +The only current event passed is a message to denote the host
>> +wants to update the layout of the screens. It contains the same
>> +info as the response to VIRTGPU_CMD_GET_DISPLAY_INFO.

I wonder if an event queue is the best mechanism if you can get the same 
info from a command anyway.  For virtio-scsi I used a queue because I 
needed to specify which target or LUN the event applied to, but here you 
do not need it and a queue is susceptible to dropped events.

Perhaps a configuration field is better, like this:

     u32 events_read;
     u32 events_clear;

A new event sets a bit in events_read and generates a configuration 
change interrupt.  The guest should never write to events_read.

Writing to events_clear has the side effect of the device doing 
"events_read &= ~events_clear".  We cannot have R/W1C fields in virtio, 
but this approximation is good enough.

When the guest receives a configuration change interrupt, it reads 
event_read.  If it is nonzero, it writes the same value it read to 
events_clear, and sends the necessary commands to the card in order to 
retrieve the event data.  It can then read again event_read, and loop if 
it is again nonzero.

Paolo

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code.
  2014-03-13  9:08     ` Gerd Hoffmann
@ 2014-03-14 11:13       ` Gerd Hoffmann
  2014-03-16 12:21         ` Michael S. Tsirkin
  0 siblings, 1 reply; 23+ messages in thread
From: Gerd Hoffmann @ 2014-03-14 11:13 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: airlied, qemu-devel, Anthony Liguori

On Do, 2014-03-13 at 10:08 +0100, Gerd Hoffmann wrote:
>   Hi,
> 
> > One thing to do is add this to the virtio spec.
> > Need help with that?
> 
> What is the authoritative spec these days?
> The virtio 1.0 draft?
> Where can I find the latest revision?

Seems to be this:

https://github.com/rustyrussell/virtio-wip.git

After figthing with texlive package splitting for a while I seem to have
all needed *.sty files installed now.  Now I'm facing this:

kpathsea: Running mktextfm Arial
/usr/share/texlive/texmf/web2c/mktexnam: Could not map source
abbreviation  for Arial.
/usr/share/texlive/texmf/web2c/mktexnam: Need to update ?
mktextfm: Running mf-nowin -progname=mf \mode:=ljfour; mag:=1;
nonstopmode; input Arial
This is METAFONT, Version 2.718281 (TeX Live 2013)


kpathsea: Running mktexmf Arial
! I can't find file `Arial'.
<*> ...e:=ljfour; mag:=1; nonstopmode; input Arial

Suggestions?

> Where should I send patches?

virtio-comments list?

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code.
  2014-03-13 10:40     ` Paolo Bonzini
@ 2014-03-14 11:18       ` Gerd Hoffmann
  2014-03-16 12:28         ` Michael S. Tsirkin
  2014-03-17  4:36       ` Dave Airlie
  1 sibling, 1 reply; 23+ messages in thread
From: Gerd Hoffmann @ 2014-03-14 11:18 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: airlied, qemu-devel, Anthony Liguori, Michael S. Tsirkin

  Hi,

> Perhaps a configuration field is better, like this:
> 
>      u32 events_read;
>      u32 events_clear;
> 
> A new event sets a bit in events_read and generates a configuration 
> change interrupt.  The guest should never write to events_read.

Makes sense to me.  I think I'll go write up things as diff against
virtio-wip, then post it for review.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code.
  2014-03-14 11:13       ` Gerd Hoffmann
@ 2014-03-16 12:21         ` Michael S. Tsirkin
  0 siblings, 0 replies; 23+ messages in thread
From: Michael S. Tsirkin @ 2014-03-16 12:21 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: airlied, qemu-devel, Anthony Liguori

On Fri, Mar 14, 2014 at 12:13:09PM +0100, Gerd Hoffmann wrote:
> On Do, 2014-03-13 at 10:08 +0100, Gerd Hoffmann wrote:
> >   Hi,
> > 
> > > One thing to do is add this to the virtio spec.
> > > Need help with that?
> > 
> > What is the authoritative spec these days?
> > The virtio 1.0 draft?
> > Where can I find the latest revision?
> 
> Seems to be this:
> 
> https://github.com/rustyrussell/virtio-wip.git
> 
> After figthing with texlive package splitting for a while I seem to have
> all needed *.sty files installed now.  Now I'm facing this:
> 
> kpathsea: Running mktextfm Arial
> /usr/share/texlive/texmf/web2c/mktexnam: Could not map source
> abbreviation  for Arial.
> /usr/share/texlive/texmf/web2c/mktexnam: Need to update ?
> mktextfm: Running mf-nowin -progname=mf \mode:=ljfour; mag:=1;
> nonstopmode; input Arial
> This is METAFONT, Version 2.718281 (TeX Live 2013)
> 
> 
> kpathsea: Running mktexmf Arial
> ! I can't find file `Arial'.
> <*> ...e:=ljfour; mag:=1; nonstopmode; input Arial
> 
> Suggestions?

Do you have tex4ht installed?

I don't think I saw this specific error, but check out
t4ht-workaround/README for a description of a known Fedora
bug and workarounds.

> > Where should I send patches?
> 
> virtio-comments list?
> 
> cheers,
>   Gerd
> 

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code.
  2014-03-14 11:18       ` Gerd Hoffmann
@ 2014-03-16 12:28         ` Michael S. Tsirkin
  0 siblings, 0 replies; 23+ messages in thread
From: Michael S. Tsirkin @ 2014-03-16 12:28 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Paolo Bonzini, qemu-devel, Anthony Liguori, airlied

On Fri, Mar 14, 2014 at 12:18:38PM +0100, Gerd Hoffmann wrote:
>   Hi,
> 
> > Perhaps a configuration field is better, like this:
> > 
> >      u32 events_read;
> >      u32 events_clear;
> > 
> > A new event sets a bit in events_read and generates a configuration 
> > change interrupt.  The guest should never write to events_read.
> 
> Makes sense to me.  I think I'll go write up things as diff against
> virtio-wip, then post it for review.
> 
> cheers,
>   Gerd
>

Net device has a similar problem with gratitious packet
sending. What we are doing there is VIRTIO_NET_S_ANNOUNCE bit
in the status field in the configuration, coupled with
VIRTIO_NET_CTRL_ANNOUNCE_ACK command to clear it.


-- 
MST

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code.
  2014-03-13 10:40     ` Paolo Bonzini
  2014-03-14 11:18       ` Gerd Hoffmann
@ 2014-03-17  4:36       ` Dave Airlie
  2014-03-17  5:21         ` Dave Airlie
                           ` (2 more replies)
  1 sibling, 3 replies; 23+ messages in thread
From: Dave Airlie @ 2014-03-17  4:36 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: qemu-devel, Dave Airlie, Gerd Hoffmann, Anthony Liguori,
	Michael S. Tsirkin

On Thu, Mar 13, 2014 at 8:40 PM, Paolo Bonzini <pbonzini@redhat.com> wrote:
> Il 12/03/2014 21:26, Michael S. Tsirkin ha scritto:
>>>
>>> +Event queue:
>>> +The only current event passed is a message to denote the host
>>> +wants to update the layout of the screens. It contains the same
>>> +info as the response to VIRTGPU_CMD_GET_DISPLAY_INFO.
>
>
> I wonder if an event queue is the best mechanism if you can get the same
> info from a command anyway.  For virtio-scsi I used a queue because I needed
> to specify which target or LUN the event applied to, but here you do not
> need it and a queue is susceptible to dropped events.
>
> Perhaps a configuration field is better, like this:
>
>     u32 events_read;
>     u32 events_clear;
>
> A new event sets a bit in events_read and generates a configuration change
> interrupt.  The guest should never write to events_read.
>
> Writing to events_clear has the side effect of the device doing "events_read
> &= ~events_clear".  We cannot have R/W1C fields in virtio, but this
> approximation is good enough.
>
> When the guest receives a configuration change interrupt, it reads
> event_read.  If it is nonzero, it writes the same value it read to
> events_clear, and sends the necessary commands to the card in order to
> retrieve the event data.  It can then read again event_read, and loop if it
> is again nonzero.
>

I steered away from using config space for anything for the normal
operation of the GPU after looking at overheads and hearing from S390
people that config space has some special properties on their hw,

The number of these events should be small in a running system, and
I'm not sure how we'd lose one.

Dave.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code.
  2014-03-17  4:36       ` Dave Airlie
@ 2014-03-17  5:21         ` Dave Airlie
  2014-03-17  9:50           ` Paolo Bonzini
  2014-03-17  9:27         ` Paolo Bonzini
  2014-03-17 11:01         ` Michael S. Tsirkin
  2 siblings, 1 reply; 23+ messages in thread
From: Dave Airlie @ 2014-03-17  5:21 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: qemu-devel, Dave Airlie, Gerd Hoffmann, Anthony Liguori,
	Michael S. Tsirkin

On Mon, Mar 17, 2014 at 2:36 PM, Dave Airlie <airlied@gmail.com> wrote:
> On Thu, Mar 13, 2014 at 8:40 PM, Paolo Bonzini <pbonzini@redhat.com> wrote:
>> Il 12/03/2014 21:26, Michael S. Tsirkin ha scritto:
>>>>
>>>> +Event queue:
>>>> +The only current event passed is a message to denote the host
>>>> +wants to update the layout of the screens. It contains the same
>>>> +info as the response to VIRTGPU_CMD_GET_DISPLAY_INFO.
>>
>>
>> I wonder if an event queue is the best mechanism if you can get the same
>> info from a command anyway.  For virtio-scsi I used a queue because I needed
>> to specify which target or LUN the event applied to, but here you do not
>> need it and a queue is susceptible to dropped events.
>>
>> Perhaps a configuration field is better, like this:
>>
>>     u32 events_read;
>>     u32 events_clear;
>>
>> A new event sets a bit in events_read and generates a configuration change
>> interrupt.  The guest should never write to events_read.
>>
>> Writing to events_clear has the side effect of the device doing "events_read
>> &= ~events_clear".  We cannot have R/W1C fields in virtio, but this
>> approximation is good enough.
>>
>> When the guest receives a configuration change interrupt, it reads
>> event_read.  If it is nonzero, it writes the same value it read to
>> events_clear, and sends the necessary commands to the card in order to
>> retrieve the event data.  It can then read again event_read, and loop if it
>> is again nonzero.
>>
>
> I steered away from using config space for anything for the normal
> operation of the GPU after looking at overheads and hearing from S390
> people that config space has some special properties on their hw,
>
> The number of these events should be small in a running system, and
> I'm not sure how we'd lose one.

Oh I was also going to use this queue to report HW error events from
the guest to the host,

like if the guest tries an illegal operation,

Dave.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code.
  2014-03-17  4:36       ` Dave Airlie
  2014-03-17  5:21         ` Dave Airlie
@ 2014-03-17  9:27         ` Paolo Bonzini
  2014-03-17 11:01         ` Michael S. Tsirkin
  2 siblings, 0 replies; 23+ messages in thread
From: Paolo Bonzini @ 2014-03-17  9:27 UTC (permalink / raw)
  To: Dave Airlie
  Cc: Dave Airlie, Michael S. Tsirkin, qemu-devel, Anthony Liguori,
	Gerd Hoffmann

Il 17/03/2014 05:36, Dave Airlie ha scritto:
> I steered away from using config space for anything for the normal
> operation of the GPU after looking at overheads and hearing from S390
> people that config space has some special properties on their hw,

Right, but this is only for the old s390-virtio implementation, and it 
means that the cursor would not work on s390-virtio either.  The new 
one, which looks like real s390 hardware, doesn't have the problem.

> The number of these events should be small in a running system, and
> I'm not sure how we'd lose one.

Are they few even if you resize the viewer window, and this causes a 
stream of display-size changes?

Paolo

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code.
  2014-03-17  5:21         ` Dave Airlie
@ 2014-03-17  9:50           ` Paolo Bonzini
  0 siblings, 0 replies; 23+ messages in thread
From: Paolo Bonzini @ 2014-03-17  9:50 UTC (permalink / raw)
  To: Dave Airlie
  Cc: qemu-devel, Dave Airlie, Gerd Hoffmann, Anthony Liguori,
	Michael S. Tsirkin

Il 17/03/2014 06:21, Dave Airlie ha scritto:
> Oh I was also going to use this queue to report HW error events from
> the guest to the host,
>
> like if the guest tries an illegal operation,

What fields would be present for such an error?

Paolo

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code.
  2014-03-17  4:36       ` Dave Airlie
  2014-03-17  5:21         ` Dave Airlie
  2014-03-17  9:27         ` Paolo Bonzini
@ 2014-03-17 11:01         ` Michael S. Tsirkin
  2 siblings, 0 replies; 23+ messages in thread
From: Michael S. Tsirkin @ 2014-03-17 11:01 UTC (permalink / raw)
  To: Dave Airlie
  Cc: qemu-devel, Paolo Bonzini, Gerd Hoffmann, Anthony Liguori, Dave Airlie

On Mon, Mar 17, 2014 at 02:36:37PM +1000, Dave Airlie wrote:
> On Thu, Mar 13, 2014 at 8:40 PM, Paolo Bonzini <pbonzini@redhat.com> wrote:
> > Il 12/03/2014 21:26, Michael S. Tsirkin ha scritto:
> >>>
> >>> +Event queue:
> >>> +The only current event passed is a message to denote the host
> >>> +wants to update the layout of the screens. It contains the same
> >>> +info as the response to VIRTGPU_CMD_GET_DISPLAY_INFO.
> >
> >
> > I wonder if an event queue is the best mechanism if you can get the same
> > info from a command anyway.  For virtio-scsi I used a queue because I needed
> > to specify which target or LUN the event applied to, but here you do not
> > need it and a queue is susceptible to dropped events.
> >
> > Perhaps a configuration field is better, like this:
> >
> >     u32 events_read;
> >     u32 events_clear;
> >
> > A new event sets a bit in events_read and generates a configuration change
> > interrupt.  The guest should never write to events_read.
> >
> > Writing to events_clear has the side effect of the device doing "events_read
> > &= ~events_clear".  We cannot have R/W1C fields in virtio, but this
> > approximation is good enough.
> >
> > When the guest receives a configuration change interrupt, it reads
> > event_read.  If it is nonzero, it writes the same value it read to
> > events_clear, and sends the necessary commands to the card in order to
> > retrieve the event data.  It can then read again event_read, and loop if it
> > is again nonzero.
> >
> 
> I steered away from using config space for anything for the normal
> operation of the GPU after looking at overheads and hearing from S390
> people that config space has some special properties on their hw,
> 
> The number of these events should be small in a running system, and
> I'm not sure how we'd lose one.
> 
> Dave.

I think Paolo refers to a case where these events
arrive faster than guest can handle them.

As long as there's a single event like this,
it seems possible to avoid such under-runs by guest,
by first making a new buffer available, then processing
a used buffer.

If we want this extendable to multiple events,
make guest prepend an out buffer with a header, saying what kind of
event is expected in a given buffer.

Yes, config space is slow and should be avoided for
normal operation. I'm not sure how common this
specific operation is. For reporting guest errors
config space has some advantages as it can
be read even if driver/device communication
is completely wedged.

-- 
MST

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2014-03-17 11:02 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-03-12 12:55 [Qemu-devel] [PATCH 0/4] vga: new display devices Gerd Hoffmann
2014-03-12 12:55 ` [Qemu-devel] [PATCH 1/4] vga: allow non-global vmstate Gerd Hoffmann
2014-03-12 12:55 ` [Qemu-devel] [PATCH 2/4] vga: add secondary stdvga variant Gerd Hoffmann
2014-03-12 13:11   ` Eric Blake
2014-03-12 12:55 ` [Qemu-devel] [PATCH 3/4] virtio-gpu: v0.3 of the virtio based GPU code Gerd Hoffmann
2014-03-12 20:26   ` Michael S. Tsirkin
2014-03-13  9:08     ` Gerd Hoffmann
2014-03-14 11:13       ` Gerd Hoffmann
2014-03-16 12:21         ` Michael S. Tsirkin
2014-03-13 10:40     ` Paolo Bonzini
2014-03-14 11:18       ` Gerd Hoffmann
2014-03-16 12:28         ` Michael S. Tsirkin
2014-03-17  4:36       ` Dave Airlie
2014-03-17  5:21         ` Dave Airlie
2014-03-17  9:50           ` Paolo Bonzini
2014-03-17  9:27         ` Paolo Bonzini
2014-03-17 11:01         ` Michael S. Tsirkin
2014-03-12 12:55 ` [Qemu-devel] [PATCH 4/4] virtio-vga: v1 Gerd Hoffmann
2014-03-12 13:55 ` [Qemu-devel] [PATCH 0/4] vga: new display devices Laszlo Ersek
2014-03-12 15:47   ` Gerd Hoffmann
2014-03-12 18:14     ` Laszlo Ersek
2014-03-13  8:22       ` Gerd Hoffmann
2014-03-13  8:41         ` Laszlo Ersek

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.