diff options
Diffstat (limited to 'hw/display/virtio-vga.c')
-rw-r--r-- | hw/display/virtio-vga.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c new file mode 100644 index 000000000..9e57f61e9 --- /dev/null +++ b/hw/display/virtio-vga.c @@ -0,0 +1,280 @@ +#include "qemu/osdep.h" +#include "hw/pci/pci.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio-gpu.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "virtio-vga.h" +#include "qom/object.h" + +static void virtio_vga_base_invalidate_display(void *opaque) +{ + VirtIOVGABase *vvga = opaque; + VirtIOGPUBase *g = vvga->vgpu; + + if (g->enable) { + g->hw_ops->invalidate(g); + } else { + vvga->vga.hw_ops->invalidate(&vvga->vga); + } +} + +static void virtio_vga_base_update_display(void *opaque) +{ + VirtIOVGABase *vvga = opaque; + VirtIOGPUBase *g = vvga->vgpu; + + if (g->enable) { + g->hw_ops->gfx_update(g); + } else { + vvga->vga.hw_ops->gfx_update(&vvga->vga); + } +} + +static void virtio_vga_base_text_update(void *opaque, console_ch_t *chardata) +{ + VirtIOVGABase *vvga = opaque; + VirtIOGPUBase *g = vvga->vgpu; + + if (g->enable) { + if (g->hw_ops->text_update) { + g->hw_ops->text_update(g, chardata); + } + } else { + if (vvga->vga.hw_ops->text_update) { + vvga->vga.hw_ops->text_update(&vvga->vga, chardata); + } + } +} + +static int virtio_vga_base_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) +{ + VirtIOVGABase *vvga = opaque; + VirtIOGPUBase *g = vvga->vgpu; + + if (g->hw_ops->ui_info) { + return g->hw_ops->ui_info(g, idx, info); + } + return -1; +} + +static void virtio_vga_base_gl_block(void *opaque, bool block) +{ + VirtIOVGABase *vvga = opaque; + VirtIOGPUBase *g = vvga->vgpu; + + if (g->hw_ops->gl_block) { + g->hw_ops->gl_block(g, block); + } +} + +static void virtio_vga_base_gl_flushed(void *opaque) +{ + VirtIOVGABase *vvga = opaque; + VirtIOGPUBase *g = vvga->vgpu; + + if (g->hw_ops->gl_flushed) { + g->hw_ops->gl_flushed(g); + } +} + +static int virtio_vga_base_get_flags(void *opaque) +{ + VirtIOVGABase *vvga = opaque; + VirtIOGPUBase *g = vvga->vgpu; + + return g->hw_ops->get_flags(g); +} + +static const GraphicHwOps virtio_vga_base_ops = { + .get_flags = virtio_vga_base_get_flags, + .invalidate = virtio_vga_base_invalidate_display, + .gfx_update = virtio_vga_base_update_display, + .text_update = virtio_vga_base_text_update, + .ui_info = virtio_vga_base_ui_info, + .gl_block = virtio_vga_base_gl_block, + .gl_flushed = virtio_vga_base_gl_flushed, +}; + +static const VMStateDescription vmstate_virtio_vga_base = { + .name = "virtio-vga", + .version_id = 2, + .minimum_version_id = 2, + .fields = (VMStateField[]) { + /* no pci stuff here, saving the virtio device will handle that */ + VMSTATE_STRUCT(vga, VirtIOVGABase, 0, + vmstate_vga_common, VGACommonState), + VMSTATE_END_OF_LIST() + } +}; + +/* VGA device wrapper around PCI device around virtio GPU */ +static void virtio_vga_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VirtIOVGABase *vvga = VIRTIO_VGA_BASE(vpci_dev); + VirtIOGPUBase *g = vvga->vgpu; + VGACommonState *vga = &vvga->vga; + uint32_t offset; + int i; + + /* init vga compat bits */ + vga->vram_size_mb = 8; + vga_common_init(vga, OBJECT(vpci_dev)); + vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev), + pci_address_space_io(&vpci_dev->pci_dev), true); + pci_register_bar(&vpci_dev->pci_dev, 0, + PCI_BASE_ADDRESS_MEM_PREFETCH, &vga->vram); + + /* + * Configure virtio bar and regions + * + * We use bar #2 for the mmio regions, to be compatible with stdvga. + * virtio regions are moved to the end of bar #2, to make room for + * the stdvga mmio registers at the start of bar #2. + */ + vpci_dev->modern_mem_bar_idx = 2; + vpci_dev->msix_bar_idx = 4; + vpci_dev->modern_io_bar_idx = 5; + + if (!(vpci_dev->flags & VIRTIO_PCI_FLAG_PAGE_PER_VQ)) { + /* + * with page-per-vq=off there is no padding space we can use + * for the stdvga registers. Make the common and isr regions + * smaller then. + */ + vpci_dev->common.size /= 2; + vpci_dev->isr.size /= 2; + } + + offset = memory_region_size(&vpci_dev->modern_bar); + offset -= vpci_dev->notify.size; + vpci_dev->notify.offset = offset; + offset -= vpci_dev->device.size; + vpci_dev->device.offset = offset; + offset -= vpci_dev->isr.size; + vpci_dev->isr.offset = offset; + offset -= vpci_dev->common.size; + vpci_dev->common.offset = offset; + + /* init virtio bits */ + virtio_pci_force_virtio_1(vpci_dev); + if (!qdev_realize(DEVICE(g), BUS(&vpci_dev->bus), errp)) { + return; + } + + /* add stdvga mmio regions */ + pci_std_vga_mmio_region_init(vga, OBJECT(vvga), &vpci_dev->modern_bar, + vvga->vga_mrs, true, false); + + vga->con = g->scanout[0].con; + graphic_console_set_hwops(vga->con, &virtio_vga_base_ops, vvga); + + for (i = 0; i < g->conf.max_outputs; i++) { + object_property_set_link(OBJECT(g->scanout[i].con), "device", + OBJECT(vpci_dev), &error_abort); + } +} + +static void virtio_vga_base_reset(DeviceState *dev) +{ + VirtIOVGABaseClass *klass = VIRTIO_VGA_BASE_GET_CLASS(dev); + VirtIOVGABase *vvga = VIRTIO_VGA_BASE(dev); + + /* reset virtio-gpu */ + klass->parent_reset(dev); + + /* reset vga */ + vga_common_reset(&vvga->vga); + vga_dirty_log_start(&vvga->vga); +} + +static bool virtio_vga_get_big_endian_fb(Object *obj, Error **errp) +{ + VirtIOVGABase *d = VIRTIO_VGA_BASE(obj); + + return d->vga.big_endian_fb; +} + +static void virtio_vga_set_big_endian_fb(Object *obj, bool value, Error **errp) +{ + VirtIOVGABase *d = VIRTIO_VGA_BASE(obj); + + d->vga.big_endian_fb = value; +} + +static Property virtio_vga_base_properties[] = { + DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_vga_base_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + VirtIOVGABaseClass *v = VIRTIO_VGA_BASE_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + device_class_set_props(dc, virtio_vga_base_properties); + dc->vmsd = &vmstate_virtio_vga_base; + dc->hotpluggable = false; + device_class_set_parent_reset(dc, virtio_vga_base_reset, + &v->parent_reset); + + k->realize = virtio_vga_base_realize; + pcidev_k->romfile = "vgabios-virtio.bin"; + pcidev_k->class_id = PCI_CLASS_DISPLAY_VGA; + + /* Expose framebuffer byteorder via QOM */ + object_class_property_add_bool(klass, "big-endian-framebuffer", + virtio_vga_get_big_endian_fb, + virtio_vga_set_big_endian_fb); +} + +static TypeInfo virtio_vga_base_info = { + .name = TYPE_VIRTIO_VGA_BASE, + .parent = TYPE_VIRTIO_PCI, + .instance_size = sizeof(VirtIOVGABase), + .class_size = sizeof(VirtIOVGABaseClass), + .class_init = virtio_vga_base_class_init, + .abstract = true, +}; +module_obj(TYPE_VIRTIO_VGA_BASE); + +#define TYPE_VIRTIO_VGA "virtio-vga" + +typedef struct VirtIOVGA VirtIOVGA; +DECLARE_INSTANCE_CHECKER(VirtIOVGA, VIRTIO_VGA, + TYPE_VIRTIO_VGA) + +struct VirtIOVGA { + VirtIOVGABase parent_obj; + + VirtIOGPU vdev; +}; + +static void virtio_vga_inst_initfn(Object *obj) +{ + VirtIOVGA *dev = VIRTIO_VGA(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_GPU); + VIRTIO_VGA_BASE(dev)->vgpu = VIRTIO_GPU_BASE(&dev->vdev); +} + + +static VirtioPCIDeviceTypeInfo virtio_vga_info = { + .generic_name = TYPE_VIRTIO_VGA, + .parent = TYPE_VIRTIO_VGA_BASE, + .instance_size = sizeof(VirtIOVGA), + .instance_init = virtio_vga_inst_initfn, +}; +module_obj(TYPE_VIRTIO_VGA); + +static void virtio_vga_register_types(void) +{ + type_register_static(&virtio_vga_base_info); + virtio_pci_types_register(&virtio_vga_info); +} + +type_init(virtio_vga_register_types) |