cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

bochs-display.c (11480B)


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