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