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 22 typedef struct BochsDisplayMode { 23 pixman_format_code_t format; 24 uint32_t bytepp; 25 uint32_t width; 26 uint32_t height; 27 uint32_t stride; 28 uint64_t offset; 29 uint64_t size; 30 } BochsDisplayMode; 31 32 typedef struct BochsDisplayState { 33 /* parent */ 34 PCIDevice pci; 35 36 /* device elements */ 37 QemuConsole *con; 38 MemoryRegion vram; 39 MemoryRegion mmio; 40 MemoryRegion vbe; 41 MemoryRegion qext; 42 MemoryRegion edid; 43 44 /* device config */ 45 uint64_t vgamem; 46 bool enable_edid; 47 qemu_edid_info edid_info; 48 uint8_t edid_blob[256]; 49 50 /* device registers */ 51 uint16_t vbe_regs[VBE_DISPI_INDEX_NB]; 52 bool big_endian_fb; 53 54 /* device state */ 55 BochsDisplayMode mode; 56 } BochsDisplayState; 57 58 #define TYPE_BOCHS_DISPLAY "bochs-display" 59 #define BOCHS_DISPLAY(obj) OBJECT_CHECK(BochsDisplayState, (obj), \ 60 TYPE_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 s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s); 271 272 if (s->vgamem < 4 * MiB) { 273 error_setg(errp, "bochs-display: video memory too small"); 274 } 275 if (s->vgamem > 256 * MiB) { 276 error_setg(errp, "bochs-display: video memory too big"); 277 } 278 s->vgamem = pow2ceil(s->vgamem); 279 280 memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem, 281 &error_fatal); 282 memory_region_init_io(&s->vbe, obj, &bochs_display_vbe_ops, s, 283 "bochs dispi interface", PCI_VGA_BOCHS_SIZE); 284 memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s, 285 "qemu extended regs", PCI_VGA_QEXT_SIZE); 286 287 memory_region_init_io(&s->mmio, obj, &unassigned_io_ops, NULL, 288 "bochs-display-mmio", PCI_VGA_MMIO_SIZE); 289 memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe); 290 memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext); 291 292 pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2); 293 pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); 294 pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); 295 296 if (s->enable_edid) { 297 qemu_edid_generate(s->edid_blob, sizeof(s->edid_blob), &s->edid_info); 298 qemu_edid_region_io(&s->edid, obj, s->edid_blob, sizeof(s->edid_blob)); 299 memory_region_add_subregion(&s->mmio, 0, &s->edid); 300 } 301 302 if (pci_bus_is_express(pci_get_bus(dev))) { 303 ret = pcie_endpoint_cap_init(dev, 0x80); 304 assert(ret > 0); 305 } else { 306 dev->cap_present &= ~QEMU_PCI_CAP_EXPRESS; 307 } 308 309 memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA); 310 } 311 312 static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp) 313 { 314 BochsDisplayState *s = BOCHS_DISPLAY(obj); 315 316 return s->big_endian_fb; 317 } 318 319 static void bochs_display_set_big_endian_fb(Object *obj, bool value, 320 Error **errp) 321 { 322 BochsDisplayState *s = BOCHS_DISPLAY(obj); 323 324 s->big_endian_fb = value; 325 } 326 327 static void bochs_display_init(Object *obj) 328 { 329 PCIDevice *dev = PCI_DEVICE(obj); 330 331 /* Expose framebuffer byteorder via QOM */ 332 object_property_add_bool(obj, "big-endian-framebuffer", 333 bochs_display_get_big_endian_fb, 334 bochs_display_set_big_endian_fb, 335 NULL); 336 337 dev->cap_present |= QEMU_PCI_CAP_EXPRESS; 338 } 339 340 static void bochs_display_exit(PCIDevice *dev) 341 { 342 BochsDisplayState *s = BOCHS_DISPLAY(dev); 343 344 graphic_console_close(s->con); 345 } 346 347 static Property bochs_display_properties[] = { 348 DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * MiB), 349 DEFINE_PROP_BOOL("edid", BochsDisplayState, enable_edid, true), 350 DEFINE_EDID_PROPERTIES(BochsDisplayState, edid_info), 351 DEFINE_PROP_END_OF_LIST(), 352 }; 353 354 static void bochs_display_class_init(ObjectClass *klass, void *data) 355 { 356 DeviceClass *dc = DEVICE_CLASS(klass); 357 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 358 359 k->class_id = PCI_CLASS_DISPLAY_OTHER; 360 k->vendor_id = PCI_VENDOR_ID_QEMU; 361 k->device_id = PCI_DEVICE_ID_QEMU_VGA; 362 363 k->realize = bochs_display_realize; 364 k->romfile = "vgabios-bochs-display.bin"; 365 k->exit = bochs_display_exit; 366 dc->vmsd = &vmstate_bochs_display; 367 device_class_set_props(dc, bochs_display_properties); 368 set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); 369 } 370 371 static const TypeInfo bochs_display_type_info = { 372 .name = TYPE_BOCHS_DISPLAY, 373 .parent = TYPE_PCI_DEVICE, 374 .instance_size = sizeof(BochsDisplayState), 375 .instance_init = bochs_display_init, 376 .class_init = bochs_display_class_init, 377 .interfaces = (InterfaceInfo[]) { 378 { INTERFACE_PCIE_DEVICE }, 379 { INTERFACE_CONVENTIONAL_PCI_DEVICE }, 380 { }, 381 }, 382 }; 383 384 static void bochs_display_register_types(void) 385 { 386 type_register_static(&bochs_display_type_info); 387 } 388 389 type_init(bochs_display_register_types) 390