2 * QEMU PCI bochs display adapter.
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.
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"
17 #include "qapi/error.h"
19 #include "ui/console.h"
20 #include "ui/qemu-pixman.h"
21 #include "qom/object.h"
23 typedef struct BochsDisplayMode
{
24 pixman_format_code_t format
;
33 struct BochsDisplayState
{
48 qemu_edid_info edid_info
;
49 uint8_t edid_blob
[256];
51 /* device registers */
52 uint16_t vbe_regs
[VBE_DISPI_INDEX_NB
];
56 BochsDisplayMode mode
;
58 typedef struct BochsDisplayState BochsDisplayState
;
60 #define TYPE_BOCHS_DISPLAY "bochs-display"
61 DECLARE_INSTANCE_CHECKER(BochsDisplayState
, BOCHS_DISPLAY
,
64 static const VMStateDescription vmstate_bochs_display
= {
65 .name
= "bochs-display",
66 .fields
= (VMStateField
[]) {
67 VMSTATE_PCI_DEVICE(pci
, BochsDisplayState
),
68 VMSTATE_UINT16_ARRAY(vbe_regs
, BochsDisplayState
, VBE_DISPI_INDEX_NB
),
69 VMSTATE_BOOL(big_endian_fb
, BochsDisplayState
),
74 static uint64_t bochs_display_vbe_read(void *ptr
, hwaddr addr
,
77 BochsDisplayState
*s
= ptr
;
78 unsigned int index
= addr
>> 1;
81 case VBE_DISPI_INDEX_ID
:
83 case VBE_DISPI_INDEX_VIDEO_MEMORY_64K
:
84 return s
->vgamem
/ (64 * KiB
);
87 if (index
>= ARRAY_SIZE(s
->vbe_regs
)) {
90 return s
->vbe_regs
[index
];
93 static void bochs_display_vbe_write(void *ptr
, hwaddr addr
,
94 uint64_t val
, unsigned size
)
96 BochsDisplayState
*s
= ptr
;
97 unsigned int index
= addr
>> 1;
99 if (index
>= ARRAY_SIZE(s
->vbe_regs
)) {
102 s
->vbe_regs
[index
] = val
;
105 static const MemoryRegionOps bochs_display_vbe_ops
= {
106 .read
= bochs_display_vbe_read
,
107 .write
= bochs_display_vbe_write
,
108 .valid
.min_access_size
= 1,
109 .valid
.max_access_size
= 4,
110 .impl
.min_access_size
= 2,
111 .impl
.max_access_size
= 2,
112 .endianness
= DEVICE_LITTLE_ENDIAN
,
115 static uint64_t bochs_display_qext_read(void *ptr
, hwaddr addr
,
118 BochsDisplayState
*s
= ptr
;
121 case PCI_VGA_QEXT_REG_SIZE
:
122 return PCI_VGA_QEXT_SIZE
;
123 case PCI_VGA_QEXT_REG_BYTEORDER
:
124 return s
->big_endian_fb
?
125 PCI_VGA_QEXT_BIG_ENDIAN
: PCI_VGA_QEXT_LITTLE_ENDIAN
;
131 static void bochs_display_qext_write(void *ptr
, hwaddr addr
,
132 uint64_t val
, unsigned size
)
134 BochsDisplayState
*s
= ptr
;
137 case PCI_VGA_QEXT_REG_BYTEORDER
:
138 if (val
== PCI_VGA_QEXT_BIG_ENDIAN
) {
139 s
->big_endian_fb
= true;
141 if (val
== PCI_VGA_QEXT_LITTLE_ENDIAN
) {
142 s
->big_endian_fb
= false;
148 static const MemoryRegionOps bochs_display_qext_ops
= {
149 .read
= bochs_display_qext_read
,
150 .write
= bochs_display_qext_write
,
151 .valid
.min_access_size
= 4,
152 .valid
.max_access_size
= 4,
153 .endianness
= DEVICE_LITTLE_ENDIAN
,
156 static int bochs_display_get_mode(BochsDisplayState
*s
,
157 BochsDisplayMode
*mode
)
159 uint16_t *vbe
= s
->vbe_regs
;
162 if (!(vbe
[VBE_DISPI_INDEX_ENABLE
] & VBE_DISPI_ENABLED
)) {
166 memset(mode
, 0, sizeof(*mode
));
167 switch (vbe
[VBE_DISPI_INDEX_BPP
]) {
169 /* best effort: support native endianess only */
170 mode
->format
= PIXMAN_r5g6b5
;
174 mode
->format
= s
->big_endian_fb
176 : PIXMAN_LE_x8r8g8b8
;
183 mode
->width
= vbe
[VBE_DISPI_INDEX_XRES
];
184 mode
->height
= vbe
[VBE_DISPI_INDEX_YRES
];
185 virt_width
= vbe
[VBE_DISPI_INDEX_VIRT_WIDTH
];
186 if (virt_width
< mode
->width
) {
187 virt_width
= mode
->width
;
189 mode
->stride
= virt_width
* mode
->bytepp
;
190 mode
->size
= (uint64_t)mode
->stride
* mode
->height
;
191 mode
->offset
= ((uint64_t)vbe
[VBE_DISPI_INDEX_X_OFFSET
] * mode
->bytepp
+
192 (uint64_t)vbe
[VBE_DISPI_INDEX_Y_OFFSET
] * mode
->stride
);
194 if (mode
->width
< 64 || mode
->height
< 64) {
197 if (mode
->offset
+ mode
->size
> s
->vgamem
) {
203 static void bochs_display_update(void *opaque
)
205 BochsDisplayState
*s
= opaque
;
206 DirtyBitmapSnapshot
*snap
= NULL
;
207 bool full_update
= false;
208 BochsDisplayMode mode
;
214 ret
= bochs_display_get_mode(s
, &mode
);
216 /* no (valid) video mode */
220 if (memcmp(&s
->mode
, &mode
, sizeof(mode
)) != 0) {
221 /* video mode switch */
223 ptr
= memory_region_get_ram_ptr(&s
->vram
);
224 ds
= qemu_create_displaysurface_from(mode
.width
,
229 dpy_gfx_replace_surface(s
->con
, ds
);
234 dpy_gfx_update_full(s
->con
);
236 snap
= memory_region_snapshot_and_clear_dirty(&s
->vram
,
237 mode
.offset
, mode
.size
,
240 for (y
= 0; y
< mode
.height
; y
++) {
241 dirty
= memory_region_snapshot_get_dirty(&s
->vram
, snap
,
242 mode
.offset
+ mode
.stride
* y
,
244 if (dirty
&& ys
< 0) {
247 if (!dirty
&& ys
>= 0) {
248 dpy_gfx_update(s
->con
, 0, ys
,
254 dpy_gfx_update(s
->con
, 0, ys
,
262 static const GraphicHwOps bochs_display_gfx_ops
= {
263 .gfx_update
= bochs_display_update
,
266 static void bochs_display_realize(PCIDevice
*dev
, Error
**errp
)
268 BochsDisplayState
*s
= BOCHS_DISPLAY(dev
);
269 Object
*obj
= OBJECT(dev
);
272 if (s
->vgamem
< 4 * MiB
) {
273 error_setg(errp
, "bochs-display: video memory too small");
276 if (s
->vgamem
> 256 * MiB
) {
277 error_setg(errp
, "bochs-display: video memory too big");
280 s
->vgamem
= pow2ceil(s
->vgamem
);
282 s
->con
= graphic_console_init(DEVICE(dev
), 0, &bochs_display_gfx_ops
, s
);
284 memory_region_init_ram(&s
->vram
, obj
, "bochs-display-vram", s
->vgamem
,
286 memory_region_init_io(&s
->vbe
, obj
, &bochs_display_vbe_ops
, s
,
287 "bochs dispi interface", PCI_VGA_BOCHS_SIZE
);
288 memory_region_init_io(&s
->qext
, obj
, &bochs_display_qext_ops
, s
,
289 "qemu extended regs", PCI_VGA_QEXT_SIZE
);
291 memory_region_init_io(&s
->mmio
, obj
, &unassigned_io_ops
, NULL
,
292 "bochs-display-mmio", PCI_VGA_MMIO_SIZE
);
293 memory_region_add_subregion(&s
->mmio
, PCI_VGA_BOCHS_OFFSET
, &s
->vbe
);
294 memory_region_add_subregion(&s
->mmio
, PCI_VGA_QEXT_OFFSET
, &s
->qext
);
296 pci_set_byte(&s
->pci
.config
[PCI_REVISION_ID
], 2);
297 pci_register_bar(&s
->pci
, 0, PCI_BASE_ADDRESS_MEM_PREFETCH
, &s
->vram
);
298 pci_register_bar(&s
->pci
, 2, PCI_BASE_ADDRESS_SPACE_MEMORY
, &s
->mmio
);
300 if (s
->enable_edid
) {
301 qemu_edid_generate(s
->edid_blob
, sizeof(s
->edid_blob
), &s
->edid_info
);
302 qemu_edid_region_io(&s
->edid
, obj
, s
->edid_blob
, sizeof(s
->edid_blob
));
303 memory_region_add_subregion(&s
->mmio
, 0, &s
->edid
);
306 if (pci_bus_is_express(pci_get_bus(dev
))) {
307 ret
= pcie_endpoint_cap_init(dev
, 0x80);
310 dev
->cap_present
&= ~QEMU_PCI_CAP_EXPRESS
;
313 memory_region_set_log(&s
->vram
, true, DIRTY_MEMORY_VGA
);
316 static bool bochs_display_get_big_endian_fb(Object
*obj
, Error
**errp
)
318 BochsDisplayState
*s
= BOCHS_DISPLAY(obj
);
320 return s
->big_endian_fb
;
323 static void bochs_display_set_big_endian_fb(Object
*obj
, bool value
,
326 BochsDisplayState
*s
= BOCHS_DISPLAY(obj
);
328 s
->big_endian_fb
= value
;
331 static void bochs_display_init(Object
*obj
)
333 PCIDevice
*dev
= PCI_DEVICE(obj
);
335 /* Expose framebuffer byteorder via QOM */
336 object_property_add_bool(obj
, "big-endian-framebuffer",
337 bochs_display_get_big_endian_fb
,
338 bochs_display_set_big_endian_fb
);
340 dev
->cap_present
|= QEMU_PCI_CAP_EXPRESS
;
343 static void bochs_display_exit(PCIDevice
*dev
)
345 BochsDisplayState
*s
= BOCHS_DISPLAY(dev
);
347 graphic_console_close(s
->con
);
350 static Property bochs_display_properties
[] = {
351 DEFINE_PROP_SIZE("vgamem", BochsDisplayState
, vgamem
, 16 * MiB
),
352 DEFINE_PROP_BOOL("edid", BochsDisplayState
, enable_edid
, true),
353 DEFINE_EDID_PROPERTIES(BochsDisplayState
, edid_info
),
354 DEFINE_PROP_END_OF_LIST(),
357 static void bochs_display_class_init(ObjectClass
*klass
, void *data
)
359 DeviceClass
*dc
= DEVICE_CLASS(klass
);
360 PCIDeviceClass
*k
= PCI_DEVICE_CLASS(klass
);
362 k
->class_id
= PCI_CLASS_DISPLAY_OTHER
;
363 k
->vendor_id
= PCI_VENDOR_ID_QEMU
;
364 k
->device_id
= PCI_DEVICE_ID_QEMU_VGA
;
366 k
->realize
= bochs_display_realize
;
367 k
->romfile
= "vgabios-bochs-display.bin";
368 k
->exit
= bochs_display_exit
;
369 dc
->vmsd
= &vmstate_bochs_display
;
370 device_class_set_props(dc
, bochs_display_properties
);
371 set_bit(DEVICE_CATEGORY_DISPLAY
, dc
->categories
);
374 static const TypeInfo bochs_display_type_info
= {
375 .name
= TYPE_BOCHS_DISPLAY
,
376 .parent
= TYPE_PCI_DEVICE
,
377 .instance_size
= sizeof(BochsDisplayState
),
378 .instance_init
= bochs_display_init
,
379 .class_init
= bochs_display_class_init
,
380 .interfaces
= (InterfaceInfo
[]) {
381 { INTERFACE_PCIE_DEVICE
},
382 { INTERFACE_CONVENTIONAL_PCI_DEVICE
},
387 static void bochs_display_register_types(void)
389 type_register_static(&bochs_display_type_info
);
392 type_init(bochs_display_register_types
)