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 typedef struct BochsDisplayState BochsDisplayState; 59 60 #define TYPE_BOCHS_DISPLAY "bochs-display" 61 DECLARE_INSTANCE_CHECKER(BochsDisplayState, BOCHS_DISPLAY, 62 TYPE_BOCHS_DISPLAY) 63 64 static const VMStateDescription vmstate_bochs_display = { 65 .name = "bochs-display", 66 .fields = (VMStateField[]) { 67 VMSTATE_PCI_DEVICE(pci, BochsDisplayState), 68 VMSTATE_UINT16_ARRAY(vbe_regs, BochsDisplayState, VBE_DISPI_INDEX_NB), 69 VMSTATE_BOOL(big_endian_fb, BochsDisplayState), 70 VMSTATE_END_OF_LIST() 71 } 72 }; 73 74 static uint64_t bochs_display_vbe_read(void *ptr, hwaddr addr, 75 unsigned size) 76 { 77 BochsDisplayState *s = ptr; 78 unsigned int index = addr >> 1; 79 80 switch (index) { 81 case VBE_DISPI_INDEX_ID: 82 return VBE_DISPI_ID5; 83 case VBE_DISPI_INDEX_VIDEO_MEMORY_64K: 84 return s->vgamem / (64 * KiB); 85 } 86 87 if (index >= ARRAY_SIZE(s->vbe_regs)) { 88 return -1; 89 } 90 return s->vbe_regs[index]; 91 } 92 93 static void bochs_display_vbe_write(void *ptr, hwaddr addr, 94 uint64_t val, unsigned size) 95 { 96 BochsDisplayState *s = ptr; 97 unsigned int index = addr >> 1; 98 99 if (index >= ARRAY_SIZE(s->vbe_regs)) { 100 return; 101 } 102 s->vbe_regs[index] = val; 103 } 104 105 static const MemoryRegionOps bochs_display_vbe_ops = { 106 .read = bochs_display_vbe_read, 107 .write = bochs_display_vbe_write, 108 .valid.min_access_size = 1, 109 .valid.max_access_size = 4, 110 .impl.min_access_size = 2, 111 .impl.max_access_size = 2, 112 .endianness = DEVICE_LITTLE_ENDIAN, 113 }; 114 115 static uint64_t bochs_display_qext_read(void *ptr, hwaddr addr, 116 unsigned size) 117 { 118 BochsDisplayState *s = ptr; 119 120 switch (addr) { 121 case PCI_VGA_QEXT_REG_SIZE: 122 return PCI_VGA_QEXT_SIZE; 123 case PCI_VGA_QEXT_REG_BYTEORDER: 124 return s->big_endian_fb ? 125 PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN; 126 default: 127 return 0; 128 } 129 } 130 131 static void bochs_display_qext_write(void *ptr, hwaddr addr, 132 uint64_t val, unsigned size) 133 { 134 BochsDisplayState *s = ptr; 135 136 switch (addr) { 137 case PCI_VGA_QEXT_REG_BYTEORDER: 138 if (val == PCI_VGA_QEXT_BIG_ENDIAN) { 139 s->big_endian_fb = true; 140 } 141 if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) { 142 s->big_endian_fb = false; 143 } 144 break; 145 } 146 } 147 148 static const MemoryRegionOps bochs_display_qext_ops = { 149 .read = bochs_display_qext_read, 150 .write = bochs_display_qext_write, 151 .valid.min_access_size = 4, 152 .valid.max_access_size = 4, 153 .endianness = DEVICE_LITTLE_ENDIAN, 154 }; 155 156 static int bochs_display_get_mode(BochsDisplayState *s, 157 BochsDisplayMode *mode) 158 { 159 uint16_t *vbe = s->vbe_regs; 160 uint32_t virt_width; 161 162 if (!(vbe[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { 163 return -1; 164 } 165 166 memset(mode, 0, sizeof(*mode)); 167 switch (vbe[VBE_DISPI_INDEX_BPP]) { 168 case 16: 169 /* best effort: support native endianess only */ 170 mode->format = PIXMAN_r5g6b5; 171 mode->bytepp = 2; 172 break; 173 case 32: 174 mode->format = s->big_endian_fb 175 ? PIXMAN_BE_x8r8g8b8 176 : PIXMAN_LE_x8r8g8b8; 177 mode->bytepp = 4; 178 break; 179 default: 180 return -1; 181 } 182 183 mode->width = vbe[VBE_DISPI_INDEX_XRES]; 184 mode->height = vbe[VBE_DISPI_INDEX_YRES]; 185 virt_width = vbe[VBE_DISPI_INDEX_VIRT_WIDTH]; 186 if (virt_width < mode->width) { 187 virt_width = mode->width; 188 } 189 mode->stride = virt_width * mode->bytepp; 190 mode->size = (uint64_t)mode->stride * mode->height; 191 mode->offset = ((uint64_t)vbe[VBE_DISPI_INDEX_X_OFFSET] * mode->bytepp + 192 (uint64_t)vbe[VBE_DISPI_INDEX_Y_OFFSET] * mode->stride); 193 194 if (mode->width < 64 || mode->height < 64) { 195 return -1; 196 } 197 if (mode->offset + mode->size > s->vgamem) { 198 return -1; 199 } 200 return 0; 201 } 202 203 static void bochs_display_update(void *opaque) 204 { 205 BochsDisplayState *s = opaque; 206 DirtyBitmapSnapshot *snap = NULL; 207 bool full_update = false; 208 BochsDisplayMode mode; 209 DisplaySurface *ds; 210 uint8_t *ptr; 211 bool dirty; 212 int y, ys, ret; 213 214 ret = bochs_display_get_mode(s, &mode); 215 if (ret < 0) { 216 /* no (valid) video mode */ 217 return; 218 } 219 220 if (memcmp(&s->mode, &mode, sizeof(mode)) != 0) { 221 /* video mode switch */ 222 s->mode = mode; 223 ptr = memory_region_get_ram_ptr(&s->vram); 224 ds = qemu_create_displaysurface_from(mode.width, 225 mode.height, 226 mode.format, 227 mode.stride, 228 ptr + mode.offset); 229 dpy_gfx_replace_surface(s->con, ds); 230 full_update = true; 231 } 232 233 if (full_update) { 234 dpy_gfx_update_full(s->con); 235 } else { 236 snap = memory_region_snapshot_and_clear_dirty(&s->vram, 237 mode.offset, mode.size, 238 DIRTY_MEMORY_VGA); 239 ys = -1; 240 for (y = 0; y < mode.height; y++) { 241 dirty = memory_region_snapshot_get_dirty(&s->vram, snap, 242 mode.offset + mode.stride * y, 243 mode.stride); 244 if (dirty && ys < 0) { 245 ys = y; 246 } 247 if (!dirty && ys >= 0) { 248 dpy_gfx_update(s->con, 0, ys, 249 mode.width, y - ys); 250 ys = -1; 251 } 252 } 253 if (ys >= 0) { 254 dpy_gfx_update(s->con, 0, ys, 255 mode.width, y - ys); 256 } 257 258 g_free(snap); 259 } 260 } 261 262 static const GraphicHwOps bochs_display_gfx_ops = { 263 .gfx_update = bochs_display_update, 264 }; 265 266 static void bochs_display_realize(PCIDevice *dev, Error **errp) 267 { 268 BochsDisplayState *s = BOCHS_DISPLAY(dev); 269 Object *obj = OBJECT(dev); 270 int ret; 271 272 if (s->vgamem < 4 * MiB) { 273 error_setg(errp, "bochs-display: video memory too small"); 274 return; 275 } 276 if (s->vgamem > 256 * MiB) { 277 error_setg(errp, "bochs-display: video memory too big"); 278 return; 279 } 280 s->vgamem = pow2ceil(s->vgamem); 281 282 s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s); 283 284 memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem, 285 &error_fatal); 286 memory_region_init_io(&s->vbe, obj, &bochs_display_vbe_ops, s, 287 "bochs dispi interface", PCI_VGA_BOCHS_SIZE); 288 memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s, 289 "qemu extended regs", PCI_VGA_QEXT_SIZE); 290 291 memory_region_init_io(&s->mmio, obj, &unassigned_io_ops, NULL, 292 "bochs-display-mmio", PCI_VGA_MMIO_SIZE); 293 memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe); 294 memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext); 295 296 pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2); 297 pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); 298 pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); 299 300 if (s->enable_edid) { 301 qemu_edid_generate(s->edid_blob, sizeof(s->edid_blob), &s->edid_info); 302 qemu_edid_region_io(&s->edid, obj, s->edid_blob, sizeof(s->edid_blob)); 303 memory_region_add_subregion(&s->mmio, 0, &s->edid); 304 } 305 306 if (pci_bus_is_express(pci_get_bus(dev))) { 307 ret = pcie_endpoint_cap_init(dev, 0x80); 308 assert(ret > 0); 309 } else { 310 dev->cap_present &= ~QEMU_PCI_CAP_EXPRESS; 311 } 312 313 memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA); 314 } 315 316 static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp) 317 { 318 BochsDisplayState *s = BOCHS_DISPLAY(obj); 319 320 return s->big_endian_fb; 321 } 322 323 static void bochs_display_set_big_endian_fb(Object *obj, bool value, 324 Error **errp) 325 { 326 BochsDisplayState *s = BOCHS_DISPLAY(obj); 327 328 s->big_endian_fb = value; 329 } 330 331 static void bochs_display_init(Object *obj) 332 { 333 PCIDevice *dev = PCI_DEVICE(obj); 334 335 /* Expose framebuffer byteorder via QOM */ 336 object_property_add_bool(obj, "big-endian-framebuffer", 337 bochs_display_get_big_endian_fb, 338 bochs_display_set_big_endian_fb); 339 340 dev->cap_present |= QEMU_PCI_CAP_EXPRESS; 341 } 342 343 static void bochs_display_exit(PCIDevice *dev) 344 { 345 BochsDisplayState *s = BOCHS_DISPLAY(dev); 346 347 graphic_console_close(s->con); 348 } 349 350 static Property bochs_display_properties[] = { 351 DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * MiB), 352 DEFINE_PROP_BOOL("edid", BochsDisplayState, enable_edid, true), 353 DEFINE_EDID_PROPERTIES(BochsDisplayState, edid_info), 354 DEFINE_PROP_END_OF_LIST(), 355 }; 356 357 static void bochs_display_class_init(ObjectClass *klass, void *data) 358 { 359 DeviceClass *dc = DEVICE_CLASS(klass); 360 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 361 362 k->class_id = PCI_CLASS_DISPLAY_OTHER; 363 k->vendor_id = PCI_VENDOR_ID_QEMU; 364 k->device_id = PCI_DEVICE_ID_QEMU_VGA; 365 366 k->realize = bochs_display_realize; 367 k->romfile = "vgabios-bochs-display.bin"; 368 k->exit = bochs_display_exit; 369 dc->vmsd = &vmstate_bochs_display; 370 device_class_set_props(dc, bochs_display_properties); 371 set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); 372 } 373 374 static const TypeInfo bochs_display_type_info = { 375 .name = TYPE_BOCHS_DISPLAY, 376 .parent = TYPE_PCI_DEVICE, 377 .instance_size = sizeof(BochsDisplayState), 378 .instance_init = bochs_display_init, 379 .class_init = bochs_display_class_init, 380 .interfaces = (InterfaceInfo[]) { 381 { INTERFACE_PCIE_DEVICE }, 382 { INTERFACE_CONVENTIONAL_PCI_DEVICE }, 383 { }, 384 }, 385 }; 386 387 static void bochs_display_register_types(void) 388 { 389 type_register_static(&bochs_display_type_info); 390 } 391 392 type_init(bochs_display_register_types) 393