xref: /qemu/hw/display/bochs-display.c (revision f2581064a68f37d3406b90f9a0929801d97b2758)
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     int ret;
257 
258     s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s);
259 
260     if (s->vgamem < (4 * 1024 * 1024)) {
261         error_setg(errp, "bochs-display: video memory too small");
262     }
263     if (s->vgamem > (256 * 1024 * 1024)) {
264         error_setg(errp, "bochs-display: video memory too big");
265     }
266     s->vgamem = pow2ceil(s->vgamem);
267 
268     memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem,
269                            &error_fatal);
270     memory_region_init_io(&s->vbe, obj, &bochs_display_vbe_ops, s,
271                           "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
272     memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s,
273                           "qemu extended regs", PCI_VGA_QEXT_SIZE);
274 
275     memory_region_init(&s->mmio, obj, "bochs-display-mmio",
276                        PCI_VGA_MMIO_SIZE);
277     memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe);
278     memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext);
279 
280     pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2);
281     pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
282     pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
283 
284     if (pci_bus_is_express(pci_get_bus(dev))) {
285         dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
286         ret = pcie_endpoint_cap_init(dev, 0x80);
287         assert(ret > 0);
288     }
289 
290     memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA);
291 }
292 
293 static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp)
294 {
295     BochsDisplayState *s = BOCHS_DISPLAY(obj);
296 
297     return s->big_endian_fb;
298 }
299 
300 static void bochs_display_set_big_endian_fb(Object *obj, bool value,
301                                             Error **errp)
302 {
303     BochsDisplayState *s = BOCHS_DISPLAY(obj);
304 
305     s->big_endian_fb = value;
306 }
307 
308 static void bochs_display_init(Object *obj)
309 {
310     /* Expose framebuffer byteorder via QOM */
311     object_property_add_bool(obj, "big-endian-framebuffer",
312                              bochs_display_get_big_endian_fb,
313                              bochs_display_set_big_endian_fb,
314                              NULL);
315 }
316 
317 static void bochs_display_exit(PCIDevice *dev)
318 {
319     BochsDisplayState *s = BOCHS_DISPLAY(dev);
320 
321     graphic_console_close(s->con);
322 }
323 
324 static Property bochs_display_properties[] = {
325     DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * 1024 * 1024),
326     DEFINE_PROP_END_OF_LIST(),
327 };
328 
329 static void bochs_display_class_init(ObjectClass *klass, void *data)
330 {
331     DeviceClass *dc = DEVICE_CLASS(klass);
332     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
333 
334     k->class_id  = PCI_CLASS_DISPLAY_OTHER;
335     k->vendor_id = PCI_VENDOR_ID_QEMU;
336     k->device_id = PCI_DEVICE_ID_QEMU_VGA;
337 
338     k->realize   = bochs_display_realize;
339     k->exit      = bochs_display_exit;
340     dc->vmsd     = &vmstate_bochs_display;
341     dc->props    = bochs_display_properties;
342     set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
343 }
344 
345 static const TypeInfo bochs_display_type_info = {
346     .name           = TYPE_BOCHS_DISPLAY,
347     .parent         = TYPE_PCI_DEVICE,
348     .instance_size  = sizeof(BochsDisplayState),
349     .instance_init  = bochs_display_init,
350     .class_init     = bochs_display_class_init,
351     .interfaces     = (InterfaceInfo[]) {
352         { INTERFACE_PCIE_DEVICE },
353         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
354         { },
355     },
356 };
357 
358 static void bochs_display_register_types(void)
359 {
360     type_register_static(&bochs_display_type_info);
361 }
362 
363 type_init(bochs_display_register_types)
364