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