123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- /*
- * 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 "qemu/module.h"
- #include "qemu/units.h"
- #include "hw/pci/pci_device.h"
- #include "hw/qdev-properties.h"
- #include "migration/vmstate.h"
- #include "hw/display/bochs-vbe.h"
- #include "hw/display/edid.h"
- #include "qapi/error.h"
- #include "ui/console.h"
- #include "ui/qemu-pixman.h"
- #include "qom/object.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;
- struct BochsDisplayState {
- /* parent */
- PCIDevice pci;
- /* device elements */
- QemuConsole *con;
- MemoryRegion vram;
- MemoryRegion mmio;
- MemoryRegion vbe;
- MemoryRegion qext;
- MemoryRegion edid;
- /* device config */
- uint64_t vgamem;
- bool enable_edid;
- qemu_edid_info edid_info;
- uint8_t edid_blob[256];
- /* device registers */
- uint16_t vbe_regs[VBE_DISPI_INDEX_NB];
- bool big_endian_fb;
- /* device state */
- BochsDisplayMode mode;
- };
- #define TYPE_BOCHS_DISPLAY "bochs-display"
- OBJECT_DECLARE_SIMPLE_TYPE(BochsDisplayState, BOCHS_DISPLAY)
- static const VMStateDescription vmstate_bochs_display = {
- .name = "bochs-display",
- .fields = (const 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 * KiB);
- }
- 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 endianness only */
- mode->format = PIXMAN_r5g6b5;
- mode->bytepp = 2;
- break;
- 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);
- }
- g_free(snap);
- }
- }
- 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;
- if (s->vgamem < 4 * MiB) {
- error_setg(errp, "bochs-display: video memory too small");
- return;
- }
- if (s->vgamem > 256 * MiB) {
- error_setg(errp, "bochs-display: video memory too big");
- return;
- }
- s->vgamem = pow2ceil(s->vgamem);
- s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s);
- 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_io(&s->mmio, obj, &unassigned_io_ops, NULL,
- "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 (s->enable_edid) {
- qemu_edid_generate(s->edid_blob, sizeof(s->edid_blob), &s->edid_info);
- qemu_edid_region_io(&s->edid, obj, s->edid_blob, sizeof(s->edid_blob));
- memory_region_add_subregion(&s->mmio, 0, &s->edid);
- }
- if (pci_bus_is_express(pci_get_bus(dev))) {
- ret = pcie_endpoint_cap_init(dev, 0x80);
- assert(ret > 0);
- } else {
- dev->cap_present &= ~QEMU_PCI_CAP_EXPRESS;
- }
- 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)
- {
- PCIDevice *dev = PCI_DEVICE(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);
- dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
- }
- static void bochs_display_exit(PCIDevice *dev)
- {
- BochsDisplayState *s = BOCHS_DISPLAY(dev);
- graphic_console_close(s->con);
- }
- static const Property bochs_display_properties[] = {
- DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * MiB),
- DEFINE_PROP_BOOL("edid", BochsDisplayState, enable_edid, true),
- DEFINE_EDID_PROPERTIES(BochsDisplayState, edid_info),
- };
- 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->romfile = "vgabios-bochs-display.bin";
- k->exit = bochs_display_exit;
- dc->vmsd = &vmstate_bochs_display;
- device_class_set_props(dc, 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)
|