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