]> git.proxmox.com Git - mirror_qemu.git/blame - hw/display/bochs-display.c
seabios: update bios and vgabios binaries
[mirror_qemu.git] / hw / display / bochs-display.c
CommitLineData
765c9429
GH
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
17typedef 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
27typedef 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
53static 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
63static 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
82static 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
94static 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
104static 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
120static 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
137static 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
145static 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;
bc820db0 161 break;
765c9429
GH
162 case 32:
163 mode->format = s->big_endian_fb
164 ? PIXMAN_BE_x8r8g8b8
165 : PIXMAN_LE_x8r8g8b8;
166 mode->bytepp = 4;
167 break;
168 default:
169 return -1;
170 }
171
172 mode->width = vbe[VBE_DISPI_INDEX_XRES];
173 mode->height = vbe[VBE_DISPI_INDEX_YRES];
174 virt_width = vbe[VBE_DISPI_INDEX_VIRT_WIDTH];
175 if (virt_width < mode->width) {
176 virt_width = mode->width;
177 }
178 mode->stride = virt_width * mode->bytepp;
179 mode->size = (uint64_t)mode->stride * mode->height;
180 mode->offset = ((uint64_t)vbe[VBE_DISPI_INDEX_X_OFFSET] * mode->bytepp +
181 (uint64_t)vbe[VBE_DISPI_INDEX_Y_OFFSET] * mode->stride);
182
183 if (mode->width < 64 || mode->height < 64) {
184 return -1;
185 }
186 if (mode->offset + mode->size > s->vgamem) {
187 return -1;
188 }
189 return 0;
190}
191
192static void bochs_display_update(void *opaque)
193{
194 BochsDisplayState *s = opaque;
33ebad54
GH
195 DirtyBitmapSnapshot *snap = NULL;
196 bool full_update = false;
765c9429
GH
197 BochsDisplayMode mode;
198 DisplaySurface *ds;
199 uint8_t *ptr;
33ebad54
GH
200 bool dirty;
201 int y, ys, ret;
765c9429
GH
202
203 ret = bochs_display_get_mode(s, &mode);
204 if (ret < 0) {
205 /* no (valid) video mode */
206 return;
207 }
208
209 if (memcmp(&s->mode, &mode, sizeof(mode)) != 0) {
210 /* video mode switch */
211 s->mode = mode;
212 ptr = memory_region_get_ram_ptr(&s->vram);
213 ds = qemu_create_displaysurface_from(mode.width,
214 mode.height,
215 mode.format,
216 mode.stride,
217 ptr + mode.offset);
218 dpy_gfx_replace_surface(s->con, ds);
33ebad54 219 full_update = true;
765c9429
GH
220 }
221
33ebad54
GH
222 if (full_update) {
223 dpy_gfx_update_full(s->con);
224 } else {
225 snap = memory_region_snapshot_and_clear_dirty(&s->vram,
226 mode.offset, mode.size,
227 DIRTY_MEMORY_VGA);
228 ys = -1;
229 for (y = 0; y < mode.height; y++) {
230 dirty = memory_region_snapshot_get_dirty(&s->vram, snap,
231 mode.offset + mode.stride * y,
232 mode.stride);
233 if (dirty && ys < 0) {
234 ys = y;
235 }
236 if (!dirty && ys >= 0) {
237 dpy_gfx_update(s->con, 0, ys,
238 mode.width, y - ys);
239 ys = -1;
240 }
241 }
242 if (ys >= 0) {
243 dpy_gfx_update(s->con, 0, ys,
244 mode.width, y - ys);
245 }
246 }
765c9429
GH
247}
248
249static const GraphicHwOps bochs_display_gfx_ops = {
250 .gfx_update = bochs_display_update,
251};
252
253static void bochs_display_realize(PCIDevice *dev, Error **errp)
254{
255 BochsDisplayState *s = BOCHS_DISPLAY(dev);
256 Object *obj = OBJECT(dev);
f2581064 257 int ret;
765c9429
GH
258
259 s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s);
260
261 if (s->vgamem < (4 * 1024 * 1024)) {
262 error_setg(errp, "bochs-display: video memory too small");
263 }
264 if (s->vgamem > (256 * 1024 * 1024)) {
265 error_setg(errp, "bochs-display: video memory too big");
266 }
267 s->vgamem = pow2ceil(s->vgamem);
268
269 memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem,
270 &error_fatal);
271 memory_region_init_io(&s->vbe, obj, &bochs_display_vbe_ops, s,
272 "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
273 memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s,
274 "qemu extended regs", PCI_VGA_QEXT_SIZE);
275
276 memory_region_init(&s->mmio, obj, "bochs-display-mmio",
277 PCI_VGA_MMIO_SIZE);
278 memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe);
279 memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext);
280
281 pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2);
282 pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
283 pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
33ebad54 284
f2581064
GH
285 if (pci_bus_is_express(pci_get_bus(dev))) {
286 dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
287 ret = pcie_endpoint_cap_init(dev, 0x80);
288 assert(ret > 0);
289 }
290
33ebad54 291 memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA);
765c9429
GH
292}
293
294static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp)
295{
296 BochsDisplayState *s = BOCHS_DISPLAY(obj);
297
298 return s->big_endian_fb;
299}
300
301static void bochs_display_set_big_endian_fb(Object *obj, bool value,
302 Error **errp)
303{
304 BochsDisplayState *s = BOCHS_DISPLAY(obj);
305
306 s->big_endian_fb = value;
307}
308
309static void bochs_display_init(Object *obj)
310{
311 /* Expose framebuffer byteorder via QOM */
312 object_property_add_bool(obj, "big-endian-framebuffer",
313 bochs_display_get_big_endian_fb,
314 bochs_display_set_big_endian_fb,
315 NULL);
316}
317
318static void bochs_display_exit(PCIDevice *dev)
319{
320 BochsDisplayState *s = BOCHS_DISPLAY(dev);
321
322 graphic_console_close(s->con);
323}
324
325static Property bochs_display_properties[] = {
326 DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * 1024 * 1024),
327 DEFINE_PROP_END_OF_LIST(),
328};
329
330static void bochs_display_class_init(ObjectClass *klass, void *data)
331{
332 DeviceClass *dc = DEVICE_CLASS(klass);
333 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
334
335 k->class_id = PCI_CLASS_DISPLAY_OTHER;
336 k->vendor_id = PCI_VENDOR_ID_QEMU;
337 k->device_id = PCI_DEVICE_ID_QEMU_VGA;
338
339 k->realize = bochs_display_realize;
340 k->exit = bochs_display_exit;
341 dc->vmsd = &vmstate_bochs_display;
342 dc->props = bochs_display_properties;
343 set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
344}
345
346static const TypeInfo bochs_display_type_info = {
347 .name = TYPE_BOCHS_DISPLAY,
348 .parent = TYPE_PCI_DEVICE,
349 .instance_size = sizeof(BochsDisplayState),
350 .instance_init = bochs_display_init,
351 .class_init = bochs_display_class_init,
352 .interfaces = (InterfaceInfo[]) {
f2581064 353 { INTERFACE_PCIE_DEVICE },
765c9429
GH
354 { INTERFACE_CONVENTIONAL_PCI_DEVICE },
355 { },
356 },
357};
358
359static void bochs_display_register_types(void)
360{
361 type_register_static(&bochs_display_type_info);
362}
363
364type_init(bochs_display_register_types)