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.
7 #include "qemu/osdep.h"
8 #include "qemu/units.h"
10 #include "hw/pci/pci.h"
11 #include "hw/display/bochs-vbe.h"
13 #include "qapi/error.h"
15 #include "ui/console.h"
16 #include "ui/qemu-pixman.h"
18 typedef struct BochsDisplayMode
{
19 pixman_format_code_t format
;
28 typedef struct BochsDisplayState
{
42 /* device registers */
43 uint16_t vbe_regs
[VBE_DISPI_INDEX_NB
];
47 BochsDisplayMode mode
;
50 #define TYPE_BOCHS_DISPLAY "bochs-display"
51 #define BOCHS_DISPLAY(obj) OBJECT_CHECK(BochsDisplayState, (obj), \
54 static const VMStateDescription vmstate_bochs_display
= {
55 .name
= "bochs-display",
56 .fields
= (VMStateField
[]) {
57 VMSTATE_PCI_DEVICE(pci
, BochsDisplayState
),
58 VMSTATE_UINT16_ARRAY(vbe_regs
, BochsDisplayState
, VBE_DISPI_INDEX_NB
),
59 VMSTATE_BOOL(big_endian_fb
, BochsDisplayState
),
64 static uint64_t bochs_display_vbe_read(void *ptr
, hwaddr addr
,
67 BochsDisplayState
*s
= ptr
;
68 unsigned int index
= addr
>> 1;
71 case VBE_DISPI_INDEX_ID
:
73 case VBE_DISPI_INDEX_VIDEO_MEMORY_64K
:
74 return s
->vgamem
/ (64 * KiB
);
77 if (index
>= ARRAY_SIZE(s
->vbe_regs
)) {
80 return s
->vbe_regs
[index
];
83 static void bochs_display_vbe_write(void *ptr
, hwaddr addr
,
84 uint64_t val
, unsigned size
)
86 BochsDisplayState
*s
= ptr
;
87 unsigned int index
= addr
>> 1;
89 if (index
>= ARRAY_SIZE(s
->vbe_regs
)) {
92 s
->vbe_regs
[index
] = val
;
95 static const MemoryRegionOps bochs_display_vbe_ops
= {
96 .read
= bochs_display_vbe_read
,
97 .write
= bochs_display_vbe_write
,
98 .valid
.min_access_size
= 1,
99 .valid
.max_access_size
= 4,
100 .impl
.min_access_size
= 2,
101 .impl
.max_access_size
= 2,
102 .endianness
= DEVICE_LITTLE_ENDIAN
,
105 static uint64_t bochs_display_qext_read(void *ptr
, hwaddr addr
,
108 BochsDisplayState
*s
= ptr
;
111 case PCI_VGA_QEXT_REG_SIZE
:
112 return PCI_VGA_QEXT_SIZE
;
113 case PCI_VGA_QEXT_REG_BYTEORDER
:
114 return s
->big_endian_fb
?
115 PCI_VGA_QEXT_BIG_ENDIAN
: PCI_VGA_QEXT_LITTLE_ENDIAN
;
121 static void bochs_display_qext_write(void *ptr
, hwaddr addr
,
122 uint64_t val
, unsigned size
)
124 BochsDisplayState
*s
= ptr
;
127 case PCI_VGA_QEXT_REG_BYTEORDER
:
128 if (val
== PCI_VGA_QEXT_BIG_ENDIAN
) {
129 s
->big_endian_fb
= true;
131 if (val
== PCI_VGA_QEXT_LITTLE_ENDIAN
) {
132 s
->big_endian_fb
= false;
138 static const MemoryRegionOps bochs_display_qext_ops
= {
139 .read
= bochs_display_qext_read
,
140 .write
= bochs_display_qext_write
,
141 .valid
.min_access_size
= 4,
142 .valid
.max_access_size
= 4,
143 .endianness
= DEVICE_LITTLE_ENDIAN
,
146 static int bochs_display_get_mode(BochsDisplayState
*s
,
147 BochsDisplayMode
*mode
)
149 uint16_t *vbe
= s
->vbe_regs
;
152 if (!(vbe
[VBE_DISPI_INDEX_ENABLE
] & VBE_DISPI_ENABLED
)) {
156 memset(mode
, 0, sizeof(*mode
));
157 switch (vbe
[VBE_DISPI_INDEX_BPP
]) {
159 /* best effort: support native endianess only */
160 mode
->format
= PIXMAN_r5g6b5
;
164 mode
->format
= s
->big_endian_fb
166 : PIXMAN_LE_x8r8g8b8
;
173 mode
->width
= vbe
[VBE_DISPI_INDEX_XRES
];
174 mode
->height
= vbe
[VBE_DISPI_INDEX_YRES
];
175 virt_width
= vbe
[VBE_DISPI_INDEX_VIRT_WIDTH
];
176 if (virt_width
< mode
->width
) {
177 virt_width
= mode
->width
;
179 mode
->stride
= virt_width
* mode
->bytepp
;
180 mode
->size
= (uint64_t)mode
->stride
* mode
->height
;
181 mode
->offset
= ((uint64_t)vbe
[VBE_DISPI_INDEX_X_OFFSET
] * mode
->bytepp
+
182 (uint64_t)vbe
[VBE_DISPI_INDEX_Y_OFFSET
] * mode
->stride
);
184 if (mode
->width
< 64 || mode
->height
< 64) {
187 if (mode
->offset
+ mode
->size
> s
->vgamem
) {
193 static void bochs_display_update(void *opaque
)
195 BochsDisplayState
*s
= opaque
;
196 DirtyBitmapSnapshot
*snap
= NULL
;
197 bool full_update
= false;
198 BochsDisplayMode mode
;
204 ret
= bochs_display_get_mode(s
, &mode
);
206 /* no (valid) video mode */
210 if (memcmp(&s
->mode
, &mode
, sizeof(mode
)) != 0) {
211 /* video mode switch */
213 ptr
= memory_region_get_ram_ptr(&s
->vram
);
214 ds
= qemu_create_displaysurface_from(mode
.width
,
219 dpy_gfx_replace_surface(s
->con
, ds
);
224 dpy_gfx_update_full(s
->con
);
226 snap
= memory_region_snapshot_and_clear_dirty(&s
->vram
,
227 mode
.offset
, mode
.size
,
230 for (y
= 0; y
< mode
.height
; y
++) {
231 dirty
= memory_region_snapshot_get_dirty(&s
->vram
, snap
,
232 mode
.offset
+ mode
.stride
* y
,
234 if (dirty
&& ys
< 0) {
237 if (!dirty
&& ys
>= 0) {
238 dpy_gfx_update(s
->con
, 0, ys
,
244 dpy_gfx_update(s
->con
, 0, ys
,
250 static const GraphicHwOps bochs_display_gfx_ops
= {
251 .gfx_update
= bochs_display_update
,
254 static void bochs_display_realize(PCIDevice
*dev
, Error
**errp
)
256 BochsDisplayState
*s
= BOCHS_DISPLAY(dev
);
257 Object
*obj
= OBJECT(dev
);
260 s
->con
= graphic_console_init(DEVICE(dev
), 0, &bochs_display_gfx_ops
, s
);
262 if (s
->vgamem
< 4 * MiB
) {
263 error_setg(errp
, "bochs-display: video memory too small");
265 if (s
->vgamem
> 256 * MiB
) {
266 error_setg(errp
, "bochs-display: video memory too big");
268 s
->vgamem
= pow2ceil(s
->vgamem
);
270 memory_region_init_ram(&s
->vram
, obj
, "bochs-display-vram", s
->vgamem
,
272 memory_region_init_io(&s
->vbe
, obj
, &bochs_display_vbe_ops
, s
,
273 "bochs dispi interface", PCI_VGA_BOCHS_SIZE
);
274 memory_region_init_io(&s
->qext
, obj
, &bochs_display_qext_ops
, s
,
275 "qemu extended regs", PCI_VGA_QEXT_SIZE
);
277 memory_region_init(&s
->mmio
, obj
, "bochs-display-mmio",
279 memory_region_add_subregion(&s
->mmio
, PCI_VGA_BOCHS_OFFSET
, &s
->vbe
);
280 memory_region_add_subregion(&s
->mmio
, PCI_VGA_QEXT_OFFSET
, &s
->qext
);
282 pci_set_byte(&s
->pci
.config
[PCI_REVISION_ID
], 2);
283 pci_register_bar(&s
->pci
, 0, PCI_BASE_ADDRESS_MEM_PREFETCH
, &s
->vram
);
284 pci_register_bar(&s
->pci
, 2, PCI_BASE_ADDRESS_SPACE_MEMORY
, &s
->mmio
);
286 if (pci_bus_is_express(pci_get_bus(dev
))) {
287 dev
->cap_present
|= QEMU_PCI_CAP_EXPRESS
;
288 ret
= pcie_endpoint_cap_init(dev
, 0x80);
292 memory_region_set_log(&s
->vram
, true, DIRTY_MEMORY_VGA
);
295 static bool bochs_display_get_big_endian_fb(Object
*obj
, Error
**errp
)
297 BochsDisplayState
*s
= BOCHS_DISPLAY(obj
);
299 return s
->big_endian_fb
;
302 static void bochs_display_set_big_endian_fb(Object
*obj
, bool value
,
305 BochsDisplayState
*s
= BOCHS_DISPLAY(obj
);
307 s
->big_endian_fb
= value
;
310 static void bochs_display_init(Object
*obj
)
312 /* Expose framebuffer byteorder via QOM */
313 object_property_add_bool(obj
, "big-endian-framebuffer",
314 bochs_display_get_big_endian_fb
,
315 bochs_display_set_big_endian_fb
,
319 static void bochs_display_exit(PCIDevice
*dev
)
321 BochsDisplayState
*s
= BOCHS_DISPLAY(dev
);
323 graphic_console_close(s
->con
);
326 static Property bochs_display_properties
[] = {
327 DEFINE_PROP_SIZE("vgamem", BochsDisplayState
, vgamem
, 16 * MiB
),
328 DEFINE_PROP_END_OF_LIST(),
331 static void bochs_display_class_init(ObjectClass
*klass
, void *data
)
333 DeviceClass
*dc
= DEVICE_CLASS(klass
);
334 PCIDeviceClass
*k
= PCI_DEVICE_CLASS(klass
);
336 k
->class_id
= PCI_CLASS_DISPLAY_OTHER
;
337 k
->vendor_id
= PCI_VENDOR_ID_QEMU
;
338 k
->device_id
= PCI_DEVICE_ID_QEMU_VGA
;
340 k
->realize
= bochs_display_realize
;
341 k
->romfile
= "vgabios-bochs-display.bin";
342 k
->exit
= bochs_display_exit
;
343 dc
->vmsd
= &vmstate_bochs_display
;
344 dc
->props
= bochs_display_properties
;
345 set_bit(DEVICE_CATEGORY_DISPLAY
, dc
->categories
);
348 static const TypeInfo bochs_display_type_info
= {
349 .name
= TYPE_BOCHS_DISPLAY
,
350 .parent
= TYPE_PCI_DEVICE
,
351 .instance_size
= sizeof(BochsDisplayState
),
352 .instance_init
= bochs_display_init
,
353 .class_init
= bochs_display_class_init
,
354 .interfaces
= (InterfaceInfo
[]) {
355 { INTERFACE_PCIE_DEVICE
},
356 { INTERFACE_CONVENTIONAL_PCI_DEVICE
},
361 static void bochs_display_register_types(void)
363 type_register_static(&bochs_display_type_info
);
366 type_init(bochs_display_register_types
)