1
2
3
4
5
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
19typedef 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
29typedef struct BochsDisplayState {
30
31 PCIDevice pci;
32
33
34 QemuConsole *con;
35 MemoryRegion vram;
36 MemoryRegion mmio;
37 MemoryRegion vbe;
38 MemoryRegion qext;
39 MemoryRegion edid;
40
41
42 uint64_t vgamem;
43 bool enable_edid;
44 qemu_edid_info edid_info;
45 uint8_t edid_blob[256];
46
47
48 uint16_t vbe_regs[VBE_DISPI_INDEX_NB];
49 bool big_endian_fb;
50
51
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
59static 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
69static 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
88static 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
100static 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
110static 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
126static 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
143static 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
151static 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
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
198static 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
212 return;
213 }
214
215 if (memcmp(&s->mode, &mode, sizeof(mode)) != 0) {
216
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
255static const GraphicHwOps bochs_display_gfx_ops = {
256 .gfx_update = bochs_display_update,
257};
258
259static 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
306static 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
313static 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
321static void bochs_display_init(Object *obj)
322{
323
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
330static void bochs_display_exit(PCIDevice *dev)
331{
332 BochsDisplayState *s = BOCHS_DISPLAY(dev);
333
334 graphic_console_close(s->con);
335}
336
337static 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
344static 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
361static 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
374static void bochs_display_register_types(void)
375{
376 type_register_static(&bochs_display_type_info);
377}
378
379type_init(bochs_display_register_types)
380