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