mirror of
				https://github.com/qemu/qemu.git
				synced 2025-10-30 19:15:42 +00:00 
			
		
		
		
	 8063396bf3
			
		
	
	
		8063396bf3
		
	
	
	
	
		
			
			This converts existing DECLARE_INSTANCE_CHECKER usage to OBJECT_DECLARE_SIMPLE_TYPE when possible. $ ./scripts/codeconverter/converter.py -i \ --pattern=AddObjectDeclareSimpleType $(git grep -l '' -- '*.[ch]') Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> Acked-by: Paul Durrant <paul@xen.org> Message-Id: <20200916182519.415636-6-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
		
			
				
	
	
		
			391 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			391 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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.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 = (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 endianess 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 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),
 | |
|     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->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)
 |