qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

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 
     23 typedef 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 
     33 struct 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"
     60 OBJECT_DECLARE_SIMPLE_TYPE(BochsDisplayState, BOCHS_DISPLAY)
     61 
     62 static 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 
     72 static 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 
     91 static 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 
    103 static 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 
    113 static 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 
    129 static 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 
    146 static 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 
    154 static 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 
    201 static 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 
    260 static const GraphicHwOps bochs_display_gfx_ops = {
    261     .gfx_update = bochs_display_update,
    262 };
    263 
    264 static 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 
    314 static 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 
    321 static 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 
    329 static 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 
    341 static void bochs_display_exit(PCIDevice *dev)
    342 {
    343     BochsDisplayState *s = BOCHS_DISPLAY(dev);
    344 
    345     graphic_console_close(s->con);
    346 }
    347 
    348 static 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 
    355 static 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 
    372 static 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 
    385 static void bochs_display_register_types(void)
    386 {
    387     type_register_static(&bochs_display_type_info);
    388 }
    389 
    390 type_init(bochs_display_register_types)