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)