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