From a89fe6c329799e47aaa1663650f076b28808e186 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 14 May 2018 12:31:17 +0200 Subject: [PATCH 1/7] vga: catch depth 0 depth == 0 is used to indicate 256 color modes. Our region calculation goes wrong in that case. So detect that and just take the safe code path we already have for the wraparound case. While being at it also catch depth == 15 (where our region size calculation goes wrong too). And make the comment more verbose, explaining what is going on here. Without this windows guest install might trigger an assert due to trying to check dirty bitmap outside the snapshot region. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1575541 Signed-off-by: Gerd Hoffmann Message-id: 20180514103117.21059-1-kraxel@redhat.com --- hw/display/vga.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/hw/display/vga.c b/hw/display/vga.c index 72181330b8..a7794f6d1f 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1480,13 +1480,28 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) s->get_resolution(s, &width, &height); disp_width = width; + depth = s->get_bpp(s); region_start = (s->start_addr * 4); region_end = region_start + (ram_addr_t)s->line_offset * height; - region_end += width * s->get_bpp(s) / 8; /* scanline length */ + region_end += width * depth / 8; /* scanline length */ region_end -= s->line_offset; - if (region_end > s->vbe_size) { - /* wraps around (can happen with cirrus vbe modes) */ + if (region_end > s->vbe_size || depth == 0 || depth == 15) { + /* + * We land here on: + * - wraps around (can happen with cirrus vbe modes) + * - depth == 0 (256 color palette video mode) + * - depth == 15 + * + * Take the safe and slow route: + * - create a dirty bitmap snapshot for all vga memory. + * - force shadowing (so all vga memory access goes + * through vga_read_*() helpers). + * + * Given this affects only vga features which are pretty much + * unused by modern guests there should be no performance + * impact. + */ region_start = 0; region_end = s->vbe_size; force_shadow = true; @@ -1520,8 +1535,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) } } - depth = s->get_bpp(s); - /* * Check whether we can share the surface with the backend * or whether we need a shadow surface. We share native From a3ee49f07511ff487f55a15b295fb9e89453ca13 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2018 18:50:53 +0200 Subject: [PATCH 2/7] vga: move bochs vbe defines to header file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a new header file, move the bochs vbe dispi interface defines to it, so they can be used outside vga code. Signed-off-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-id: 20180522165058.15404-2-kraxel@redhat.com --- hw/display/vga-pci.c | 13 ------- hw/display/vga_int.h | 35 +---------------- include/hw/display/bochs-vbe.h | 69 ++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 46 deletions(-) create mode 100644 include/hw/display/bochs-vbe.h diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index f312930664..fb3e4cd400 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -31,19 +31,6 @@ #include "qemu/timer.h" #include "hw/loader.h" -#define PCI_VGA_IOPORT_OFFSET 0x400 -#define PCI_VGA_IOPORT_SIZE (0x3e0 - 0x3c0) -#define PCI_VGA_BOCHS_OFFSET 0x500 -#define PCI_VGA_BOCHS_SIZE (0x0b * 2) -#define PCI_VGA_QEXT_OFFSET 0x600 -#define PCI_VGA_QEXT_SIZE (2 * 4) -#define PCI_VGA_MMIO_SIZE 0x1000 - -#define PCI_VGA_QEXT_REG_SIZE (0 * 4) -#define PCI_VGA_QEXT_REG_BYTEORDER (1 * 4) -#define PCI_VGA_QEXT_LITTLE_ENDIAN 0x1e1e1e1e -#define PCI_VGA_QEXT_BIG_ENDIAN 0xbebebebe - enum vga_pci_flags { PCI_VGA_FLAG_ENABLE_MMIO = 1, PCI_VGA_FLAG_ENABLE_QEXT = 2, diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index fe23b81442..313cff84fc 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -29,42 +29,11 @@ #include "exec/memory.h" #include "ui/console.h" +#include "hw/display/bochs-vbe.h" + #define ST01_V_RETRACE 0x08 #define ST01_DISP_ENABLE 0x01 -#define VBE_DISPI_MAX_XRES 16000 -#define VBE_DISPI_MAX_YRES 12000 -#define VBE_DISPI_MAX_BPP 32 - -#define VBE_DISPI_INDEX_ID 0x0 -#define VBE_DISPI_INDEX_XRES 0x1 -#define VBE_DISPI_INDEX_YRES 0x2 -#define VBE_DISPI_INDEX_BPP 0x3 -#define VBE_DISPI_INDEX_ENABLE 0x4 -#define VBE_DISPI_INDEX_BANK 0x5 -#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 -#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 -#define VBE_DISPI_INDEX_X_OFFSET 0x8 -#define VBE_DISPI_INDEX_Y_OFFSET 0x9 -#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */ -#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */ - -#define VBE_DISPI_ID0 0xB0C0 -#define VBE_DISPI_ID1 0xB0C1 -#define VBE_DISPI_ID2 0xB0C2 -#define VBE_DISPI_ID3 0xB0C3 -#define VBE_DISPI_ID4 0xB0C4 -#define VBE_DISPI_ID5 0xB0C5 - -#define VBE_DISPI_DISABLED 0x00 -#define VBE_DISPI_ENABLED 0x01 -#define VBE_DISPI_GETCAPS 0x02 -#define VBE_DISPI_8BIT_DAC 0x20 -#define VBE_DISPI_LFB_ENABLED 0x40 -#define VBE_DISPI_NOCLEARMEM 0x80 - -#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 - #define CH_ATTR_SIZE (160 * 100) #define VGA_MAX_HEIGHT 2048 diff --git a/include/hw/display/bochs-vbe.h b/include/hw/display/bochs-vbe.h new file mode 100644 index 0000000000..bc2f046eee --- /dev/null +++ b/include/hw/display/bochs-vbe.h @@ -0,0 +1,69 @@ +#ifndef HW_DISPLAY_BOCHS_VBE_H +#define HW_DISPLAY_BOCHS_VBE_H + +/* + * bochs vesa bios extension interface + */ + +#define VBE_DISPI_MAX_XRES 16000 +#define VBE_DISPI_MAX_YRES 12000 +#define VBE_DISPI_MAX_BPP 32 + +#define VBE_DISPI_INDEX_ID 0x0 +#define VBE_DISPI_INDEX_XRES 0x1 +#define VBE_DISPI_INDEX_YRES 0x2 +#define VBE_DISPI_INDEX_BPP 0x3 +#define VBE_DISPI_INDEX_ENABLE 0x4 +#define VBE_DISPI_INDEX_BANK 0x5 +#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 +#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 +#define VBE_DISPI_INDEX_X_OFFSET 0x8 +#define VBE_DISPI_INDEX_Y_OFFSET 0x9 +#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */ +#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */ + +/* VBE_DISPI_INDEX_ID */ +#define VBE_DISPI_ID0 0xB0C0 +#define VBE_DISPI_ID1 0xB0C1 +#define VBE_DISPI_ID2 0xB0C2 +#define VBE_DISPI_ID3 0xB0C3 +#define VBE_DISPI_ID4 0xB0C4 +#define VBE_DISPI_ID5 0xB0C5 + +/* VBE_DISPI_INDEX_ENABLE */ +#define VBE_DISPI_DISABLED 0x00 +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_GETCAPS 0x02 +#define VBE_DISPI_8BIT_DAC 0x20 +#define VBE_DISPI_LFB_ENABLED 0x40 +#define VBE_DISPI_NOCLEARMEM 0x80 + +/* only used by isa-vga, pci vga devices use a memory bar */ +#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 + + +/* + * qemu extension: mmio bar (region 2) + */ + +#define PCI_VGA_MMIO_SIZE 0x1000 + +/* vga register region */ +#define PCI_VGA_IOPORT_OFFSET 0x400 +#define PCI_VGA_IOPORT_SIZE (0x3e0 - 0x3c0) + +/* bochs vbe register region */ +#define PCI_VGA_BOCHS_OFFSET 0x500 +#define PCI_VGA_BOCHS_SIZE (0x0b * 2) + +/* qemu extension register region */ +#define PCI_VGA_QEXT_OFFSET 0x600 +#define PCI_VGA_QEXT_SIZE (2 * 4) + +/* qemu extension registers */ +#define PCI_VGA_QEXT_REG_SIZE (0 * 4) +#define PCI_VGA_QEXT_REG_BYTEORDER (1 * 4) +#define PCI_VGA_QEXT_LITTLE_ENDIAN 0x1e1e1e1e +#define PCI_VGA_QEXT_BIG_ENDIAN 0xbebebebe + +#endif /* HW_DISPLAY_BOCHS_VBE_H */ From 83ff909f93d35368832230c1a4e70c84793a695c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2018 18:50:54 +0200 Subject: [PATCH 3/7] vga-pci: use PCI_VGA_MMIO_SIZE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180522165058.15404-3-kraxel@redhat.com --- hw/display/vga-pci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index fb3e4cd400..700ac58c69 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -232,7 +232,8 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) /* mmio bar for vga register access */ if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_MMIO)) { - memory_region_init(&d->mmio, NULL, "vga.mmio", 4096); + memory_region_init(&d->mmio, NULL, "vga.mmio", + PCI_VGA_MMIO_SIZE); if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { qext = true; @@ -267,7 +268,8 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) 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(&d->mmio, OBJECT(dev), "vga.mmio", + PCI_VGA_MMIO_SIZE); if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { qext = true; From 765c94290863eef1fc4a67819d452cc13b7854a1 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2018 18:50:55 +0200 Subject: [PATCH 4/7] hw/display: add new bochs-display device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After writing up the virtual mdev device emulating a display supporting the bochs vbe dispi interface (mbochs.ko) and seeing how simple it actually is I've figured that would be useful for qemu too. So, here it is, -device bochs-display. It is basically -device VGA without legacy vga emulation. PCI bar 0 is the framebuffer, PCI bar 2 is mmio with the registers. The vga registers are simply not there though, neither in the legacy ioport location nor in the mmio bar. Consequently it is PCI class DISPLAY_OTHER not DISPLAY_VGA. So there is no text mode emulation, no weird video modes (planar, 256color palette), no memory window at 0xa0000. Just a linear framebuffer in the pci memory bar. And the amount of code to emulate this (and therefore the attack surface) is an order of magnitude smaller when compared to vga emulation. Compatibility wise it works with OVMF (latest git master). The bochs-drm.ko linux kernel module can handle it just fine too. So UEFI guests should not see any functional difference to VGA. Signed-off-by: Gerd Hoffmann Tested-by: Marc-André Lureau Reviewed-by: Marc-André Lureau Message-id: 20180522165058.15404-4-kraxel@redhat.com --- hw/display/Makefile.objs | 1 + hw/display/bochs-display.c | 325 +++++++++++++++++++++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 hw/display/bochs-display.c diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 11321e466b..d907b381ae 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -9,6 +9,7 @@ common-obj-$(CONFIG_SSD0323) += ssd0323.o common-obj-$(CONFIG_XEN) += xenfb.o common-obj-$(CONFIG_VGA_PCI) += vga-pci.o +common-obj-$(CONFIG_VGA_PCI) += bochs-display.o common-obj-$(CONFIG_VGA_ISA) += vga-isa.o common-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o common-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c new file mode 100644 index 0000000000..a523e81d1d --- /dev/null +++ b/hw/display/bochs-display.c @@ -0,0 +1,325 @@ +/* + * QEMU PCI bochs display adapter. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/display/bochs-vbe.h" + +#include "qapi/error.h" + +#include "ui/console.h" +#include "ui/qemu-pixman.h" + +typedef struct BochsDisplayMode { + pixman_format_code_t format; + uint32_t bytepp; + uint32_t width; + uint32_t height; + uint32_t stride; + uint64_t offset; + uint64_t size; +} BochsDisplayMode; + +typedef struct BochsDisplayState { + /* parent */ + PCIDevice pci; + + /* device elements */ + QemuConsole *con; + MemoryRegion vram; + MemoryRegion mmio; + MemoryRegion vbe; + MemoryRegion qext; + + /* device config */ + uint64_t vgamem; + + /* device registers */ + uint16_t vbe_regs[VBE_DISPI_INDEX_NB]; + bool big_endian_fb; + + /* device state */ + BochsDisplayMode mode; +} BochsDisplayState; + +#define TYPE_BOCHS_DISPLAY "bochs-display" +#define BOCHS_DISPLAY(obj) OBJECT_CHECK(BochsDisplayState, (obj), \ + TYPE_BOCHS_DISPLAY) + +static const VMStateDescription vmstate_bochs_display = { + .name = "bochs-display", + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(pci, BochsDisplayState), + VMSTATE_UINT16_ARRAY(vbe_regs, BochsDisplayState, VBE_DISPI_INDEX_NB), + VMSTATE_BOOL(big_endian_fb, BochsDisplayState), + VMSTATE_END_OF_LIST() + } +}; + +static uint64_t bochs_display_vbe_read(void *ptr, hwaddr addr, + unsigned size) +{ + BochsDisplayState *s = ptr; + unsigned int index = addr >> 1; + + switch (index) { + case VBE_DISPI_INDEX_ID: + return VBE_DISPI_ID5; + case VBE_DISPI_INDEX_VIDEO_MEMORY_64K: + return s->vgamem / (64 * 1024); + } + + if (index >= ARRAY_SIZE(s->vbe_regs)) { + return -1; + } + return s->vbe_regs[index]; +} + +static void bochs_display_vbe_write(void *ptr, hwaddr addr, + uint64_t val, unsigned size) +{ + BochsDisplayState *s = ptr; + unsigned int index = addr >> 1; + + if (index >= ARRAY_SIZE(s->vbe_regs)) { + return; + } + s->vbe_regs[index] = val; +} + +static const MemoryRegionOps bochs_display_vbe_ops = { + .read = bochs_display_vbe_read, + .write = bochs_display_vbe_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 2, + .impl.max_access_size = 2, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t bochs_display_qext_read(void *ptr, hwaddr addr, + unsigned size) +{ + BochsDisplayState *s = ptr; + + switch (addr) { + case PCI_VGA_QEXT_REG_SIZE: + return PCI_VGA_QEXT_SIZE; + case PCI_VGA_QEXT_REG_BYTEORDER: + return s->big_endian_fb ? + PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN; + default: + return 0; + } +} + +static void bochs_display_qext_write(void *ptr, hwaddr addr, + uint64_t val, unsigned size) +{ + BochsDisplayState *s = ptr; + + switch (addr) { + case PCI_VGA_QEXT_REG_BYTEORDER: + if (val == PCI_VGA_QEXT_BIG_ENDIAN) { + s->big_endian_fb = true; + } + if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) { + s->big_endian_fb = false; + } + break; + } +} + +static const MemoryRegionOps bochs_display_qext_ops = { + .read = bochs_display_qext_read, + .write = bochs_display_qext_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static int bochs_display_get_mode(BochsDisplayState *s, + BochsDisplayMode *mode) +{ + uint16_t *vbe = s->vbe_regs; + uint32_t virt_width; + + if (!(vbe[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { + return -1; + } + + memset(mode, 0, sizeof(*mode)); + switch (vbe[VBE_DISPI_INDEX_BPP]) { + case 16: + /* best effort: support native endianess only */ + mode->format = PIXMAN_r5g6b5; + mode->bytepp = 2; + case 32: + mode->format = s->big_endian_fb + ? PIXMAN_BE_x8r8g8b8 + : PIXMAN_LE_x8r8g8b8; + mode->bytepp = 4; + break; + default: + return -1; + } + + mode->width = vbe[VBE_DISPI_INDEX_XRES]; + mode->height = vbe[VBE_DISPI_INDEX_YRES]; + virt_width = vbe[VBE_DISPI_INDEX_VIRT_WIDTH]; + if (virt_width < mode->width) { + virt_width = mode->width; + } + mode->stride = virt_width * mode->bytepp; + mode->size = (uint64_t)mode->stride * mode->height; + mode->offset = ((uint64_t)vbe[VBE_DISPI_INDEX_X_OFFSET] * mode->bytepp + + (uint64_t)vbe[VBE_DISPI_INDEX_Y_OFFSET] * mode->stride); + + if (mode->width < 64 || mode->height < 64) { + return -1; + } + if (mode->offset + mode->size > s->vgamem) { + return -1; + } + return 0; +} + +static void bochs_display_update(void *opaque) +{ + BochsDisplayState *s = opaque; + BochsDisplayMode mode; + DisplaySurface *ds; + uint8_t *ptr; + int ret; + + ret = bochs_display_get_mode(s, &mode); + if (ret < 0) { + /* no (valid) video mode */ + return; + } + + if (memcmp(&s->mode, &mode, sizeof(mode)) != 0) { + /* video mode switch */ + s->mode = mode; + ptr = memory_region_get_ram_ptr(&s->vram); + ds = qemu_create_displaysurface_from(mode.width, + mode.height, + mode.format, + mode.stride, + ptr + mode.offset); + dpy_gfx_replace_surface(s->con, ds); + } + + dpy_gfx_update_full(s->con); +} + +static const GraphicHwOps bochs_display_gfx_ops = { + .gfx_update = bochs_display_update, +}; + +static void bochs_display_realize(PCIDevice *dev, Error **errp) +{ + BochsDisplayState *s = BOCHS_DISPLAY(dev); + Object *obj = OBJECT(dev); + + s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s); + + if (s->vgamem < (4 * 1024 * 1024)) { + error_setg(errp, "bochs-display: video memory too small"); + } + if (s->vgamem > (256 * 1024 * 1024)) { + error_setg(errp, "bochs-display: video memory too big"); + } + s->vgamem = pow2ceil(s->vgamem); + + memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem, + &error_fatal); + memory_region_init_io(&s->vbe, obj, &bochs_display_vbe_ops, s, + "bochs dispi interface", PCI_VGA_BOCHS_SIZE); + memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s, + "qemu extended regs", PCI_VGA_QEXT_SIZE); + + memory_region_init(&s->mmio, obj, "bochs-display-mmio", + PCI_VGA_MMIO_SIZE); + memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe); + memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext); + + pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2); + pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); + pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); +} + +static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp) +{ + BochsDisplayState *s = BOCHS_DISPLAY(obj); + + return s->big_endian_fb; +} + +static void bochs_display_set_big_endian_fb(Object *obj, bool value, + Error **errp) +{ + BochsDisplayState *s = BOCHS_DISPLAY(obj); + + s->big_endian_fb = value; +} + +static void bochs_display_init(Object *obj) +{ + /* Expose framebuffer byteorder via QOM */ + object_property_add_bool(obj, "big-endian-framebuffer", + bochs_display_get_big_endian_fb, + bochs_display_set_big_endian_fb, + NULL); +} + +static void bochs_display_exit(PCIDevice *dev) +{ + BochsDisplayState *s = BOCHS_DISPLAY(dev); + + graphic_console_close(s->con); +} + +static Property bochs_display_properties[] = { + DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * 1024 * 1024), + DEFINE_PROP_END_OF_LIST(), +}; + +static void bochs_display_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->class_id = PCI_CLASS_DISPLAY_OTHER; + k->vendor_id = PCI_VENDOR_ID_QEMU; + k->device_id = PCI_DEVICE_ID_QEMU_VGA; + + k->realize = bochs_display_realize; + k->exit = bochs_display_exit; + dc->vmsd = &vmstate_bochs_display; + dc->props = bochs_display_properties; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); +} + +static const TypeInfo bochs_display_type_info = { + .name = TYPE_BOCHS_DISPLAY, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(BochsDisplayState), + .instance_init = bochs_display_init, + .class_init = bochs_display_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +static void bochs_display_register_types(void) +{ + type_register_static(&bochs_display_type_info); +} + +type_init(bochs_display_register_types) From 33ebad54056f0e54b9db1d675d34eed097479c69 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2018 18:50:56 +0200 Subject: [PATCH 5/7] bochs-display: add dirty tracking support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Message-id: 20180522165058.15404-5-kraxel@redhat.com --- hw/display/bochs-display.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index a523e81d1d..9607df1c34 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -191,10 +191,13 @@ static int bochs_display_get_mode(BochsDisplayState *s, static void bochs_display_update(void *opaque) { BochsDisplayState *s = opaque; + DirtyBitmapSnapshot *snap = NULL; + bool full_update = false; BochsDisplayMode mode; DisplaySurface *ds; uint8_t *ptr; - int ret; + bool dirty; + int y, ys, ret; ret = bochs_display_get_mode(s, &mode); if (ret < 0) { @@ -212,9 +215,34 @@ static void bochs_display_update(void *opaque) mode.stride, ptr + mode.offset); dpy_gfx_replace_surface(s->con, ds); + full_update = true; } - dpy_gfx_update_full(s->con); + if (full_update) { + dpy_gfx_update_full(s->con); + } else { + snap = memory_region_snapshot_and_clear_dirty(&s->vram, + mode.offset, mode.size, + DIRTY_MEMORY_VGA); + ys = -1; + for (y = 0; y < mode.height; y++) { + dirty = memory_region_snapshot_get_dirty(&s->vram, snap, + mode.offset + mode.stride * y, + mode.stride); + if (dirty && ys < 0) { + ys = y; + } + if (!dirty && ys >= 0) { + dpy_gfx_update(s->con, 0, ys, + mode.width, y - ys); + ys = -1; + } + } + if (ys >= 0) { + dpy_gfx_update(s->con, 0, ys, + mode.width, y - ys); + } + } } static const GraphicHwOps bochs_display_gfx_ops = { @@ -251,6 +279,8 @@ static void bochs_display_realize(PCIDevice *dev, Error **errp) pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2); pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); + + memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA); } static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp) From f2581064a68f37d3406b90f9a0929801d97b2758 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2018 18:50:57 +0200 Subject: [PATCH 6/7] bochs-display: add pcie support Signed-off-by: Gerd Hoffmann Message-id: 20180522165058.15404-6-kraxel@redhat.com --- hw/display/bochs-display.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index 9607df1c34..c33524b558 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -253,6 +253,7 @@ static void bochs_display_realize(PCIDevice *dev, Error **errp) { BochsDisplayState *s = BOCHS_DISPLAY(dev); Object *obj = OBJECT(dev); + int ret; s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s); @@ -280,6 +281,12 @@ static void bochs_display_realize(PCIDevice *dev, Error **errp) pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); + if (pci_bus_is_express(pci_get_bus(dev))) { + dev->cap_present |= QEMU_PCI_CAP_EXPRESS; + ret = pcie_endpoint_cap_init(dev, 0x80); + assert(ret > 0); + } + memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA); } @@ -342,6 +349,7 @@ static const TypeInfo bochs_display_type_info = { .instance_init = bochs_display_init, .class_init = bochs_display_class_init, .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, From dbb2e4726c76dbffb39efe6623bf75d2ec1db8bc Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2018 18:50:58 +0200 Subject: [PATCH 7/7] MAINTAINERS: add vga entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add entries for standard vga, virtio-gpu and cirrus. Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180522165058.15404-7-kraxel@redhat.com --- MAINTAINERS | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index e187b1f18f..2af3ad5172 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1314,6 +1314,27 @@ S: Maintained F: include/hw/misc/unimp.h F: hw/misc/unimp.c +Standard VGA +M: Gerd Hoffmann +S: Maintained +F: hw/display/vga* +F: hw/display/bochs-display.c +F: include/hw/display/vga.h +F: include/hw/display/bochs-vbe.h + +virtio-gpu +M: Gerd Hoffmann +S: Maintained +F: hw/display/virtio-gpu* +F: hw/display/virtio-vga.c +F: include/hw/virtio/virtio-gpu.h + +Cirrus VGA +M: Gerd Hoffmann +S: Odd Fixes +W: https://www.kraxel.org/blog/2014/10/qemu-using-cirrus-considered-harmful/ +F: hw/display/cirrus* + Subsystems ---------- Audio