qemu/hw/display/bochs-display.c
<<
>>
Prefs
   1/*
   2 * QEMU PCI bochs display adapter.
   3 *
   4 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   5 * See the COPYING file in the top-level directory.
   6 */
   7#include "qemu/osdep.h"
   8#include "qemu/units.h"
   9#include "hw/hw.h"
  10#include "hw/pci/pci.h"
  11#include "hw/display/bochs-vbe.h"
  12#include "hw/display/edid.h"
  13
  14#include "qapi/error.h"
  15
  16#include "ui/console.h"
  17#include "ui/qemu-pixman.h"
  18
  19typedef struct BochsDisplayMode {
  20    pixman_format_code_t format;
  21    uint32_t             bytepp;
  22    uint32_t             width;
  23    uint32_t             height;
  24    uint32_t             stride;
  25    uint64_t             offset;
  26    uint64_t             size;
  27} BochsDisplayMode;
  28
  29typedef struct BochsDisplayState {
  30    /* parent */
  31    PCIDevice        pci;
  32
  33    /* device elements */
  34    QemuConsole      *con;
  35    MemoryRegion     vram;
  36    MemoryRegion     mmio;
  37    MemoryRegion     vbe;
  38    MemoryRegion     qext;
  39    MemoryRegion     edid;
  40
  41    /* device config */
  42    uint64_t         vgamem;
  43    bool             enable_edid;
  44    qemu_edid_info   edid_info;
  45    uint8_t          edid_blob[256];
  46
  47    /* device registers */
  48    uint16_t         vbe_regs[VBE_DISPI_INDEX_NB];
  49    bool             big_endian_fb;
  50
  51    /* device state */
  52    BochsDisplayMode mode;
  53} BochsDisplayState;
  54
  55#define TYPE_BOCHS_DISPLAY "bochs-display"
  56#define BOCHS_DISPLAY(obj) OBJECT_CHECK(BochsDisplayState, (obj), \
  57                                        TYPE_BOCHS_DISPLAY)
  58
  59static const VMStateDescription vmstate_bochs_display = {
  60    .name = "bochs-display",
  61    .fields = (VMStateField[]) {
  62        VMSTATE_PCI_DEVICE(pci, BochsDisplayState),
  63        VMSTATE_UINT16_ARRAY(vbe_regs, BochsDisplayState, VBE_DISPI_INDEX_NB),
  64        VMSTATE_BOOL(big_endian_fb, BochsDisplayState),
  65        VMSTATE_END_OF_LIST()
  66    }
  67};
  68
  69static uint64_t bochs_display_vbe_read(void *ptr, hwaddr addr,
  70                                       unsigned size)
  71{
  72    BochsDisplayState *s = ptr;
  73    unsigned int index = addr >> 1;
  74
  75    switch (index) {
  76    case VBE_DISPI_INDEX_ID:
  77        return VBE_DISPI_ID5;
  78    case VBE_DISPI_INDEX_VIDEO_MEMORY_64K:
  79        return s->vgamem / (64 * KiB);
  80    }
  81
  82    if (index >= ARRAY_SIZE(s->vbe_regs)) {
  83        return -1;
  84    }
  85    return s->vbe_regs[index];
  86}
  87
  88static void bochs_display_vbe_write(void *ptr, hwaddr addr,
  89                                    uint64_t val, unsigned size)
  90{
  91    BochsDisplayState *s = ptr;
  92    unsigned int index = addr >> 1;
  93
  94    if (index >= ARRAY_SIZE(s->vbe_regs)) {
  95        return;
  96    }
  97    s->vbe_regs[index] = val;
  98}
  99
 100static const MemoryRegionOps bochs_display_vbe_ops = {
 101    .read = bochs_display_vbe_read,
 102    .write = bochs_display_vbe_write,
 103    .valid.min_access_size = 1,
 104    .valid.max_access_size = 4,
 105    .impl.min_access_size = 2,
 106    .impl.max_access_size = 2,
 107    .endianness = DEVICE_LITTLE_ENDIAN,
 108};
 109
 110static uint64_t bochs_display_qext_read(void *ptr, hwaddr addr,
 111                                        unsigned size)
 112{
 113    BochsDisplayState *s = ptr;
 114
 115    switch (addr) {
 116    case PCI_VGA_QEXT_REG_SIZE:
 117        return PCI_VGA_QEXT_SIZE;
 118    case PCI_VGA_QEXT_REG_BYTEORDER:
 119        return s->big_endian_fb ?
 120            PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN;
 121    default:
 122        return 0;
 123    }
 124}
 125
 126static void bochs_display_qext_write(void *ptr, hwaddr addr,
 127                                     uint64_t val, unsigned size)
 128{
 129    BochsDisplayState *s = ptr;
 130
 131    switch (addr) {
 132    case PCI_VGA_QEXT_REG_BYTEORDER:
 133        if (val == PCI_VGA_QEXT_BIG_ENDIAN) {
 134            s->big_endian_fb = true;
 135        }
 136        if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) {
 137            s->big_endian_fb = false;
 138        }
 139        break;
 140    }
 141}
 142
 143static const MemoryRegionOps bochs_display_qext_ops = {
 144    .read = bochs_display_qext_read,
 145    .write = bochs_display_qext_write,
 146    .valid.min_access_size = 4,
 147    .valid.max_access_size = 4,
 148    .endianness = DEVICE_LITTLE_ENDIAN,
 149};
 150
 151static int bochs_display_get_mode(BochsDisplayState *s,
 152                                   BochsDisplayMode *mode)
 153{
 154    uint16_t *vbe = s->vbe_regs;
 155    uint32_t virt_width;
 156
 157    if (!(vbe[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
 158        return -1;
 159    }
 160
 161    memset(mode, 0, sizeof(*mode));
 162    switch (vbe[VBE_DISPI_INDEX_BPP]) {
 163    case 16:
 164        /* best effort: support native endianess only */
 165        mode->format = PIXMAN_r5g6b5;
 166        mode->bytepp = 2;
 167        break;
 168    case 32:
 169        mode->format = s->big_endian_fb
 170            ? PIXMAN_BE_x8r8g8b8
 171            : PIXMAN_LE_x8r8g8b8;
 172        mode->bytepp = 4;
 173        break;
 174    default:
 175        return -1;
 176    }
 177
 178    mode->width  = vbe[VBE_DISPI_INDEX_XRES];
 179    mode->height = vbe[VBE_DISPI_INDEX_YRES];
 180    virt_width  = vbe[VBE_DISPI_INDEX_VIRT_WIDTH];
 181    if (virt_width < mode->width) {
 182        virt_width = mode->width;
 183    }
 184    mode->stride = virt_width * mode->bytepp;
 185    mode->size   = (uint64_t)mode->stride * mode->height;
 186    mode->offset = ((uint64_t)vbe[VBE_DISPI_INDEX_X_OFFSET] * mode->bytepp +
 187                    (uint64_t)vbe[VBE_DISPI_INDEX_Y_OFFSET] * mode->stride);
 188
 189    if (mode->width < 64 || mode->height < 64) {
 190        return -1;
 191    }
 192    if (mode->offset + mode->size > s->vgamem) {
 193        return -1;
 194    }
 195    return 0;
 196}
 197
 198static void bochs_display_update(void *opaque)
 199{
 200    BochsDisplayState *s = opaque;
 201    DirtyBitmapSnapshot *snap = NULL;
 202    bool full_update = false;
 203    BochsDisplayMode mode;
 204    DisplaySurface *ds;
 205    uint8_t *ptr;
 206    bool dirty;
 207    int y, ys, ret;
 208
 209    ret = bochs_display_get_mode(s, &mode);
 210    if (ret < 0) {
 211        /* no (valid) video mode */
 212        return;
 213    }
 214
 215    if (memcmp(&s->mode, &mode, sizeof(mode)) != 0) {
 216        /* video mode switch */
 217        s->mode = mode;
 218        ptr = memory_region_get_ram_ptr(&s->vram);
 219        ds = qemu_create_displaysurface_from(mode.width,
 220                                             mode.height,
 221                                             mode.format,
 222                                             mode.stride,
 223                                             ptr + mode.offset);
 224        dpy_gfx_replace_surface(s->con, ds);
 225        full_update = true;
 226    }
 227
 228    if (full_update) {
 229        dpy_gfx_update_full(s->con);
 230    } else {
 231        snap = memory_region_snapshot_and_clear_dirty(&s->vram,
 232                                                      mode.offset, mode.size,
 233                                                      DIRTY_MEMORY_VGA);
 234        ys = -1;
 235        for (y = 0; y < mode.height; y++) {
 236            dirty = memory_region_snapshot_get_dirty(&s->vram, snap,
 237                                                     mode.offset + mode.stride * y,
 238                                                     mode.stride);
 239            if (dirty && ys < 0) {
 240                ys = y;
 241            }
 242            if (!dirty && ys >= 0) {
 243                dpy_gfx_update(s->con, 0, ys,
 244                               mode.width, y - ys);
 245                ys = -1;
 246            }
 247        }
 248        if (ys >= 0) {
 249            dpy_gfx_update(s->con, 0, ys,
 250                           mode.width, y - ys);
 251        }
 252    }
 253}
 254
 255static const GraphicHwOps bochs_display_gfx_ops = {
 256    .gfx_update = bochs_display_update,
 257};
 258
 259static void bochs_display_realize(PCIDevice *dev, Error **errp)
 260{
 261    BochsDisplayState *s = BOCHS_DISPLAY(dev);
 262    Object *obj = OBJECT(dev);
 263    int ret;
 264
 265    s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s);
 266
 267    if (s->vgamem < 4 * MiB) {
 268        error_setg(errp, "bochs-display: video memory too small");
 269    }
 270    if (s->vgamem > 256 * MiB) {
 271        error_setg(errp, "bochs-display: video memory too big");
 272    }
 273    s->vgamem = pow2ceil(s->vgamem);
 274
 275    memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem,
 276                           &error_fatal);
 277    memory_region_init_io(&s->vbe, obj, &bochs_display_vbe_ops, s,
 278                          "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
 279    memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s,
 280                          "qemu extended regs", PCI_VGA_QEXT_SIZE);
 281
 282    memory_region_init(&s->mmio, obj, "bochs-display-mmio",
 283                       PCI_VGA_MMIO_SIZE);
 284    memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe);
 285    memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext);
 286
 287    pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2);
 288    pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
 289    pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
 290
 291    if (s->enable_edid) {
 292        qemu_edid_generate(s->edid_blob, sizeof(s->edid_blob), &s->edid_info);
 293        qemu_edid_region_io(&s->edid, obj, s->edid_blob, sizeof(s->edid_blob));
 294        memory_region_add_subregion(&s->mmio, 0, &s->edid);
 295    }
 296
 297    if (pci_bus_is_express(pci_get_bus(dev))) {
 298        dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
 299        ret = pcie_endpoint_cap_init(dev, 0x80);
 300        assert(ret > 0);
 301    }
 302
 303    memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA);
 304}
 305
 306static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp)
 307{
 308    BochsDisplayState *s = BOCHS_DISPLAY(obj);
 309
 310    return s->big_endian_fb;
 311}
 312
 313static void bochs_display_set_big_endian_fb(Object *obj, bool value,
 314                                            Error **errp)
 315{
 316    BochsDisplayState *s = BOCHS_DISPLAY(obj);
 317
 318    s->big_endian_fb = value;
 319}
 320
 321static void bochs_display_init(Object *obj)
 322{
 323    /* Expose framebuffer byteorder via QOM */
 324    object_property_add_bool(obj, "big-endian-framebuffer",
 325                             bochs_display_get_big_endian_fb,
 326                             bochs_display_set_big_endian_fb,
 327                             NULL);
 328}
 329
 330static void bochs_display_exit(PCIDevice *dev)
 331{
 332    BochsDisplayState *s = BOCHS_DISPLAY(dev);
 333
 334    graphic_console_close(s->con);
 335}
 336
 337static Property bochs_display_properties[] = {
 338    DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * MiB),
 339    DEFINE_PROP_BOOL("edid", BochsDisplayState, enable_edid, false),
 340    DEFINE_EDID_PROPERTIES(BochsDisplayState, edid_info),
 341    DEFINE_PROP_END_OF_LIST(),
 342};
 343
 344static void bochs_display_class_init(ObjectClass *klass, void *data)
 345{
 346    DeviceClass *dc = DEVICE_CLASS(klass);
 347    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
 348
 349    k->class_id  = PCI_CLASS_DISPLAY_OTHER;
 350    k->vendor_id = PCI_VENDOR_ID_QEMU;
 351    k->device_id = PCI_DEVICE_ID_QEMU_VGA;
 352
 353    k->realize   = bochs_display_realize;
 354    k->romfile   = "vgabios-bochs-display.bin";
 355    k->exit      = bochs_display_exit;
 356    dc->vmsd     = &vmstate_bochs_display;
 357    dc->props    = bochs_display_properties;
 358    set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
 359}
 360
 361static const TypeInfo bochs_display_type_info = {
 362    .name           = TYPE_BOCHS_DISPLAY,
 363    .parent         = TYPE_PCI_DEVICE,
 364    .instance_size  = sizeof(BochsDisplayState),
 365    .instance_init  = bochs_display_init,
 366    .class_init     = bochs_display_class_init,
 367    .interfaces     = (InterfaceInfo[]) {
 368        { INTERFACE_PCIE_DEVICE },
 369        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
 370        { },
 371    },
 372};
 373
 374static void bochs_display_register_types(void)
 375{
 376    type_register_static(&bochs_display_type_info);
 377}
 378
 379type_init(bochs_display_register_types)
 380