Commit 62b9b076 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/kraxel/tags/vga-20180524-pull-request' into staging



vga: catch depth 0
hw/display: add new bochs-display device
some cleanups.

# gpg: Signature made Thu 24 May 2018 16:45:46 BST
# gpg:                using RSA key 4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>"
# gpg:                 aka "Gerd Hoffmann <gerd@kraxel.org>"
# gpg:                 aka "Gerd Hoffmann (private) <kraxel@gmail.com>"
# Primary key fingerprint: A032 8CFF B93A 17A7 9901  FE7D 4CB6 D8EE D3E8 7138

* remotes/kraxel/tags/vga-20180524-pull-request:
  MAINTAINERS: add vga entries
  bochs-display: add pcie support
  bochs-display: add dirty tracking support
  hw/display: add new bochs-display device
  vga-pci: use PCI_VGA_MMIO_SIZE
  vga: move bochs vbe defines to header file
  vga: catch depth 0

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 45eabb2e dbb2e472
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -1314,6 +1314,27 @@ S: Maintained
F: include/hw/misc/unimp.h
F: hw/misc/unimp.c

Standard VGA
M: Gerd Hoffmann <kraxel@redhat.com>
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 <kraxel@redhat.com>
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 <kraxel@redhat.com>
S: Odd Fixes
W: https://www.kraxel.org/blog/2014/10/qemu-using-cirrus-considered-harmful/
F: hw/display/cirrus*

Subsystems
----------
Audio
+1 −0
Original line number Diff line number Diff line
@@ -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
+363 −0
Original line number Diff line number Diff line
/*
 * 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;
    DirtyBitmapSnapshot *snap = NULL;
    bool full_update = false;
    BochsDisplayMode mode;
    DisplaySurface *ds;
    uint8_t *ptr;
    bool dirty;
    int y, ys, 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);
        full_update = true;
    }

    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 = {
    .gfx_update = bochs_display_update,
};

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);

    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);

    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);
}

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_PCIE_DEVICE },
        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
        { },
    },
};

static void bochs_display_register_types(void)
{
    type_register_static(&bochs_display_type_info);
}

type_init(bochs_display_register_types)
+4 −15
Original line number Diff line number Diff line
@@ -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,
@@ -245,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;
@@ -280,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;
+18 −5
Original line number Diff line number Diff line
@@ -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
Loading