xref: /qemu/hw/display/bochs-display.c (revision 765c94290863eef1fc4a67819d452cc13b7854a1)
1*765c9429SGerd Hoffmann /*
2*765c9429SGerd Hoffmann  * QEMU PCI bochs display adapter.
3*765c9429SGerd Hoffmann  *
4*765c9429SGerd Hoffmann  * This work is licensed under the terms of the GNU GPL, version 2 or later.
5*765c9429SGerd Hoffmann  * See the COPYING file in the top-level directory.
6*765c9429SGerd Hoffmann  */
7*765c9429SGerd Hoffmann #include "qemu/osdep.h"
8*765c9429SGerd Hoffmann #include "hw/hw.h"
9*765c9429SGerd Hoffmann #include "hw/pci/pci.h"
10*765c9429SGerd Hoffmann #include "hw/display/bochs-vbe.h"
11*765c9429SGerd Hoffmann 
12*765c9429SGerd Hoffmann #include "qapi/error.h"
13*765c9429SGerd Hoffmann 
14*765c9429SGerd Hoffmann #include "ui/console.h"
15*765c9429SGerd Hoffmann #include "ui/qemu-pixman.h"
16*765c9429SGerd Hoffmann 
17*765c9429SGerd Hoffmann typedef struct BochsDisplayMode {
18*765c9429SGerd Hoffmann     pixman_format_code_t format;
19*765c9429SGerd Hoffmann     uint32_t             bytepp;
20*765c9429SGerd Hoffmann     uint32_t             width;
21*765c9429SGerd Hoffmann     uint32_t             height;
22*765c9429SGerd Hoffmann     uint32_t             stride;
23*765c9429SGerd Hoffmann     uint64_t             offset;
24*765c9429SGerd Hoffmann     uint64_t             size;
25*765c9429SGerd Hoffmann } BochsDisplayMode;
26*765c9429SGerd Hoffmann 
27*765c9429SGerd Hoffmann typedef struct BochsDisplayState {
28*765c9429SGerd Hoffmann     /* parent */
29*765c9429SGerd Hoffmann     PCIDevice        pci;
30*765c9429SGerd Hoffmann 
31*765c9429SGerd Hoffmann     /* device elements */
32*765c9429SGerd Hoffmann     QemuConsole      *con;
33*765c9429SGerd Hoffmann     MemoryRegion     vram;
34*765c9429SGerd Hoffmann     MemoryRegion     mmio;
35*765c9429SGerd Hoffmann     MemoryRegion     vbe;
36*765c9429SGerd Hoffmann     MemoryRegion     qext;
37*765c9429SGerd Hoffmann 
38*765c9429SGerd Hoffmann     /* device config */
39*765c9429SGerd Hoffmann     uint64_t         vgamem;
40*765c9429SGerd Hoffmann 
41*765c9429SGerd Hoffmann     /* device registers */
42*765c9429SGerd Hoffmann     uint16_t         vbe_regs[VBE_DISPI_INDEX_NB];
43*765c9429SGerd Hoffmann     bool             big_endian_fb;
44*765c9429SGerd Hoffmann 
45*765c9429SGerd Hoffmann     /* device state */
46*765c9429SGerd Hoffmann     BochsDisplayMode mode;
47*765c9429SGerd Hoffmann } BochsDisplayState;
48*765c9429SGerd Hoffmann 
49*765c9429SGerd Hoffmann #define TYPE_BOCHS_DISPLAY "bochs-display"
50*765c9429SGerd Hoffmann #define BOCHS_DISPLAY(obj) OBJECT_CHECK(BochsDisplayState, (obj), \
51*765c9429SGerd Hoffmann                                         TYPE_BOCHS_DISPLAY)
52*765c9429SGerd Hoffmann 
53*765c9429SGerd Hoffmann static const VMStateDescription vmstate_bochs_display = {
54*765c9429SGerd Hoffmann     .name = "bochs-display",
55*765c9429SGerd Hoffmann     .fields = (VMStateField[]) {
56*765c9429SGerd Hoffmann         VMSTATE_PCI_DEVICE(pci, BochsDisplayState),
57*765c9429SGerd Hoffmann         VMSTATE_UINT16_ARRAY(vbe_regs, BochsDisplayState, VBE_DISPI_INDEX_NB),
58*765c9429SGerd Hoffmann         VMSTATE_BOOL(big_endian_fb, BochsDisplayState),
59*765c9429SGerd Hoffmann         VMSTATE_END_OF_LIST()
60*765c9429SGerd Hoffmann     }
61*765c9429SGerd Hoffmann };
62*765c9429SGerd Hoffmann 
63*765c9429SGerd Hoffmann static uint64_t bochs_display_vbe_read(void *ptr, hwaddr addr,
64*765c9429SGerd Hoffmann                                        unsigned size)
65*765c9429SGerd Hoffmann {
66*765c9429SGerd Hoffmann     BochsDisplayState *s = ptr;
67*765c9429SGerd Hoffmann     unsigned int index = addr >> 1;
68*765c9429SGerd Hoffmann 
69*765c9429SGerd Hoffmann     switch (index) {
70*765c9429SGerd Hoffmann     case VBE_DISPI_INDEX_ID:
71*765c9429SGerd Hoffmann         return VBE_DISPI_ID5;
72*765c9429SGerd Hoffmann     case VBE_DISPI_INDEX_VIDEO_MEMORY_64K:
73*765c9429SGerd Hoffmann         return s->vgamem / (64 * 1024);
74*765c9429SGerd Hoffmann     }
75*765c9429SGerd Hoffmann 
76*765c9429SGerd Hoffmann     if (index >= ARRAY_SIZE(s->vbe_regs)) {
77*765c9429SGerd Hoffmann         return -1;
78*765c9429SGerd Hoffmann     }
79*765c9429SGerd Hoffmann     return s->vbe_regs[index];
80*765c9429SGerd Hoffmann }
81*765c9429SGerd Hoffmann 
82*765c9429SGerd Hoffmann static void bochs_display_vbe_write(void *ptr, hwaddr addr,
83*765c9429SGerd Hoffmann                                     uint64_t val, unsigned size)
84*765c9429SGerd Hoffmann {
85*765c9429SGerd Hoffmann     BochsDisplayState *s = ptr;
86*765c9429SGerd Hoffmann     unsigned int index = addr >> 1;
87*765c9429SGerd Hoffmann 
88*765c9429SGerd Hoffmann     if (index >= ARRAY_SIZE(s->vbe_regs)) {
89*765c9429SGerd Hoffmann         return;
90*765c9429SGerd Hoffmann     }
91*765c9429SGerd Hoffmann     s->vbe_regs[index] = val;
92*765c9429SGerd Hoffmann }
93*765c9429SGerd Hoffmann 
94*765c9429SGerd Hoffmann static const MemoryRegionOps bochs_display_vbe_ops = {
95*765c9429SGerd Hoffmann     .read = bochs_display_vbe_read,
96*765c9429SGerd Hoffmann     .write = bochs_display_vbe_write,
97*765c9429SGerd Hoffmann     .valid.min_access_size = 1,
98*765c9429SGerd Hoffmann     .valid.max_access_size = 4,
99*765c9429SGerd Hoffmann     .impl.min_access_size = 2,
100*765c9429SGerd Hoffmann     .impl.max_access_size = 2,
101*765c9429SGerd Hoffmann     .endianness = DEVICE_LITTLE_ENDIAN,
102*765c9429SGerd Hoffmann };
103*765c9429SGerd Hoffmann 
104*765c9429SGerd Hoffmann static uint64_t bochs_display_qext_read(void *ptr, hwaddr addr,
105*765c9429SGerd Hoffmann                                         unsigned size)
106*765c9429SGerd Hoffmann {
107*765c9429SGerd Hoffmann     BochsDisplayState *s = ptr;
108*765c9429SGerd Hoffmann 
109*765c9429SGerd Hoffmann     switch (addr) {
110*765c9429SGerd Hoffmann     case PCI_VGA_QEXT_REG_SIZE:
111*765c9429SGerd Hoffmann         return PCI_VGA_QEXT_SIZE;
112*765c9429SGerd Hoffmann     case PCI_VGA_QEXT_REG_BYTEORDER:
113*765c9429SGerd Hoffmann         return s->big_endian_fb ?
114*765c9429SGerd Hoffmann             PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN;
115*765c9429SGerd Hoffmann     default:
116*765c9429SGerd Hoffmann         return 0;
117*765c9429SGerd Hoffmann     }
118*765c9429SGerd Hoffmann }
119*765c9429SGerd Hoffmann 
120*765c9429SGerd Hoffmann static void bochs_display_qext_write(void *ptr, hwaddr addr,
121*765c9429SGerd Hoffmann                                      uint64_t val, unsigned size)
122*765c9429SGerd Hoffmann {
123*765c9429SGerd Hoffmann     BochsDisplayState *s = ptr;
124*765c9429SGerd Hoffmann 
125*765c9429SGerd Hoffmann     switch (addr) {
126*765c9429SGerd Hoffmann     case PCI_VGA_QEXT_REG_BYTEORDER:
127*765c9429SGerd Hoffmann         if (val == PCI_VGA_QEXT_BIG_ENDIAN) {
128*765c9429SGerd Hoffmann             s->big_endian_fb = true;
129*765c9429SGerd Hoffmann         }
130*765c9429SGerd Hoffmann         if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) {
131*765c9429SGerd Hoffmann             s->big_endian_fb = false;
132*765c9429SGerd Hoffmann         }
133*765c9429SGerd Hoffmann         break;
134*765c9429SGerd Hoffmann     }
135*765c9429SGerd Hoffmann }
136*765c9429SGerd Hoffmann 
137*765c9429SGerd Hoffmann static const MemoryRegionOps bochs_display_qext_ops = {
138*765c9429SGerd Hoffmann     .read = bochs_display_qext_read,
139*765c9429SGerd Hoffmann     .write = bochs_display_qext_write,
140*765c9429SGerd Hoffmann     .valid.min_access_size = 4,
141*765c9429SGerd Hoffmann     .valid.max_access_size = 4,
142*765c9429SGerd Hoffmann     .endianness = DEVICE_LITTLE_ENDIAN,
143*765c9429SGerd Hoffmann };
144*765c9429SGerd Hoffmann 
145*765c9429SGerd Hoffmann static int bochs_display_get_mode(BochsDisplayState *s,
146*765c9429SGerd Hoffmann                                    BochsDisplayMode *mode)
147*765c9429SGerd Hoffmann {
148*765c9429SGerd Hoffmann     uint16_t *vbe = s->vbe_regs;
149*765c9429SGerd Hoffmann     uint32_t virt_width;
150*765c9429SGerd Hoffmann 
151*765c9429SGerd Hoffmann     if (!(vbe[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
152*765c9429SGerd Hoffmann         return -1;
153*765c9429SGerd Hoffmann     }
154*765c9429SGerd Hoffmann 
155*765c9429SGerd Hoffmann     memset(mode, 0, sizeof(*mode));
156*765c9429SGerd Hoffmann     switch (vbe[VBE_DISPI_INDEX_BPP]) {
157*765c9429SGerd Hoffmann     case 16:
158*765c9429SGerd Hoffmann         /* best effort: support native endianess only */
159*765c9429SGerd Hoffmann         mode->format = PIXMAN_r5g6b5;
160*765c9429SGerd Hoffmann         mode->bytepp = 2;
161*765c9429SGerd Hoffmann     case 32:
162*765c9429SGerd Hoffmann         mode->format = s->big_endian_fb
163*765c9429SGerd Hoffmann             ? PIXMAN_BE_x8r8g8b8
164*765c9429SGerd Hoffmann             : PIXMAN_LE_x8r8g8b8;
165*765c9429SGerd Hoffmann         mode->bytepp = 4;
166*765c9429SGerd Hoffmann         break;
167*765c9429SGerd Hoffmann     default:
168*765c9429SGerd Hoffmann         return -1;
169*765c9429SGerd Hoffmann     }
170*765c9429SGerd Hoffmann 
171*765c9429SGerd Hoffmann     mode->width  = vbe[VBE_DISPI_INDEX_XRES];
172*765c9429SGerd Hoffmann     mode->height = vbe[VBE_DISPI_INDEX_YRES];
173*765c9429SGerd Hoffmann     virt_width  = vbe[VBE_DISPI_INDEX_VIRT_WIDTH];
174*765c9429SGerd Hoffmann     if (virt_width < mode->width) {
175*765c9429SGerd Hoffmann         virt_width = mode->width;
176*765c9429SGerd Hoffmann     }
177*765c9429SGerd Hoffmann     mode->stride = virt_width * mode->bytepp;
178*765c9429SGerd Hoffmann     mode->size   = (uint64_t)mode->stride * mode->height;
179*765c9429SGerd Hoffmann     mode->offset = ((uint64_t)vbe[VBE_DISPI_INDEX_X_OFFSET] * mode->bytepp +
180*765c9429SGerd Hoffmann                     (uint64_t)vbe[VBE_DISPI_INDEX_Y_OFFSET] * mode->stride);
181*765c9429SGerd Hoffmann 
182*765c9429SGerd Hoffmann     if (mode->width < 64 || mode->height < 64) {
183*765c9429SGerd Hoffmann         return -1;
184*765c9429SGerd Hoffmann     }
185*765c9429SGerd Hoffmann     if (mode->offset + mode->size > s->vgamem) {
186*765c9429SGerd Hoffmann         return -1;
187*765c9429SGerd Hoffmann     }
188*765c9429SGerd Hoffmann     return 0;
189*765c9429SGerd Hoffmann }
190*765c9429SGerd Hoffmann 
191*765c9429SGerd Hoffmann static void bochs_display_update(void *opaque)
192*765c9429SGerd Hoffmann {
193*765c9429SGerd Hoffmann     BochsDisplayState *s = opaque;
194*765c9429SGerd Hoffmann     BochsDisplayMode mode;
195*765c9429SGerd Hoffmann     DisplaySurface *ds;
196*765c9429SGerd Hoffmann     uint8_t *ptr;
197*765c9429SGerd Hoffmann     int ret;
198*765c9429SGerd Hoffmann 
199*765c9429SGerd Hoffmann     ret = bochs_display_get_mode(s, &mode);
200*765c9429SGerd Hoffmann     if (ret < 0) {
201*765c9429SGerd Hoffmann         /* no (valid) video mode */
202*765c9429SGerd Hoffmann         return;
203*765c9429SGerd Hoffmann     }
204*765c9429SGerd Hoffmann 
205*765c9429SGerd Hoffmann     if (memcmp(&s->mode, &mode, sizeof(mode)) != 0) {
206*765c9429SGerd Hoffmann         /* video mode switch */
207*765c9429SGerd Hoffmann         s->mode = mode;
208*765c9429SGerd Hoffmann         ptr = memory_region_get_ram_ptr(&s->vram);
209*765c9429SGerd Hoffmann         ds = qemu_create_displaysurface_from(mode.width,
210*765c9429SGerd Hoffmann                                              mode.height,
211*765c9429SGerd Hoffmann                                              mode.format,
212*765c9429SGerd Hoffmann                                              mode.stride,
213*765c9429SGerd Hoffmann                                              ptr + mode.offset);
214*765c9429SGerd Hoffmann         dpy_gfx_replace_surface(s->con, ds);
215*765c9429SGerd Hoffmann     }
216*765c9429SGerd Hoffmann 
217*765c9429SGerd Hoffmann     dpy_gfx_update_full(s->con);
218*765c9429SGerd Hoffmann }
219*765c9429SGerd Hoffmann 
220*765c9429SGerd Hoffmann static const GraphicHwOps bochs_display_gfx_ops = {
221*765c9429SGerd Hoffmann     .gfx_update = bochs_display_update,
222*765c9429SGerd Hoffmann };
223*765c9429SGerd Hoffmann 
224*765c9429SGerd Hoffmann static void bochs_display_realize(PCIDevice *dev, Error **errp)
225*765c9429SGerd Hoffmann {
226*765c9429SGerd Hoffmann     BochsDisplayState *s = BOCHS_DISPLAY(dev);
227*765c9429SGerd Hoffmann     Object *obj = OBJECT(dev);
228*765c9429SGerd Hoffmann 
229*765c9429SGerd Hoffmann     s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s);
230*765c9429SGerd Hoffmann 
231*765c9429SGerd Hoffmann     if (s->vgamem < (4 * 1024 * 1024)) {
232*765c9429SGerd Hoffmann         error_setg(errp, "bochs-display: video memory too small");
233*765c9429SGerd Hoffmann     }
234*765c9429SGerd Hoffmann     if (s->vgamem > (256 * 1024 * 1024)) {
235*765c9429SGerd Hoffmann         error_setg(errp, "bochs-display: video memory too big");
236*765c9429SGerd Hoffmann     }
237*765c9429SGerd Hoffmann     s->vgamem = pow2ceil(s->vgamem);
238*765c9429SGerd Hoffmann 
239*765c9429SGerd Hoffmann     memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem,
240*765c9429SGerd Hoffmann                            &error_fatal);
241*765c9429SGerd Hoffmann     memory_region_init_io(&s->vbe, obj, &bochs_display_vbe_ops, s,
242*765c9429SGerd Hoffmann                           "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
243*765c9429SGerd Hoffmann     memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s,
244*765c9429SGerd Hoffmann                           "qemu extended regs", PCI_VGA_QEXT_SIZE);
245*765c9429SGerd Hoffmann 
246*765c9429SGerd Hoffmann     memory_region_init(&s->mmio, obj, "bochs-display-mmio",
247*765c9429SGerd Hoffmann                        PCI_VGA_MMIO_SIZE);
248*765c9429SGerd Hoffmann     memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe);
249*765c9429SGerd Hoffmann     memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext);
250*765c9429SGerd Hoffmann 
251*765c9429SGerd Hoffmann     pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2);
252*765c9429SGerd Hoffmann     pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
253*765c9429SGerd Hoffmann     pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
254*765c9429SGerd Hoffmann }
255*765c9429SGerd Hoffmann 
256*765c9429SGerd Hoffmann static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp)
257*765c9429SGerd Hoffmann {
258*765c9429SGerd Hoffmann     BochsDisplayState *s = BOCHS_DISPLAY(obj);
259*765c9429SGerd Hoffmann 
260*765c9429SGerd Hoffmann     return s->big_endian_fb;
261*765c9429SGerd Hoffmann }
262*765c9429SGerd Hoffmann 
263*765c9429SGerd Hoffmann static void bochs_display_set_big_endian_fb(Object *obj, bool value,
264*765c9429SGerd Hoffmann                                             Error **errp)
265*765c9429SGerd Hoffmann {
266*765c9429SGerd Hoffmann     BochsDisplayState *s = BOCHS_DISPLAY(obj);
267*765c9429SGerd Hoffmann 
268*765c9429SGerd Hoffmann     s->big_endian_fb = value;
269*765c9429SGerd Hoffmann }
270*765c9429SGerd Hoffmann 
271*765c9429SGerd Hoffmann static void bochs_display_init(Object *obj)
272*765c9429SGerd Hoffmann {
273*765c9429SGerd Hoffmann     /* Expose framebuffer byteorder via QOM */
274*765c9429SGerd Hoffmann     object_property_add_bool(obj, "big-endian-framebuffer",
275*765c9429SGerd Hoffmann                              bochs_display_get_big_endian_fb,
276*765c9429SGerd Hoffmann                              bochs_display_set_big_endian_fb,
277*765c9429SGerd Hoffmann                              NULL);
278*765c9429SGerd Hoffmann }
279*765c9429SGerd Hoffmann 
280*765c9429SGerd Hoffmann static void bochs_display_exit(PCIDevice *dev)
281*765c9429SGerd Hoffmann {
282*765c9429SGerd Hoffmann     BochsDisplayState *s = BOCHS_DISPLAY(dev);
283*765c9429SGerd Hoffmann 
284*765c9429SGerd Hoffmann     graphic_console_close(s->con);
285*765c9429SGerd Hoffmann }
286*765c9429SGerd Hoffmann 
287*765c9429SGerd Hoffmann static Property bochs_display_properties[] = {
288*765c9429SGerd Hoffmann     DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * 1024 * 1024),
289*765c9429SGerd Hoffmann     DEFINE_PROP_END_OF_LIST(),
290*765c9429SGerd Hoffmann };
291*765c9429SGerd Hoffmann 
292*765c9429SGerd Hoffmann static void bochs_display_class_init(ObjectClass *klass, void *data)
293*765c9429SGerd Hoffmann {
294*765c9429SGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
295*765c9429SGerd Hoffmann     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
296*765c9429SGerd Hoffmann 
297*765c9429SGerd Hoffmann     k->class_id  = PCI_CLASS_DISPLAY_OTHER;
298*765c9429SGerd Hoffmann     k->vendor_id = PCI_VENDOR_ID_QEMU;
299*765c9429SGerd Hoffmann     k->device_id = PCI_DEVICE_ID_QEMU_VGA;
300*765c9429SGerd Hoffmann 
301*765c9429SGerd Hoffmann     k->realize   = bochs_display_realize;
302*765c9429SGerd Hoffmann     k->exit      = bochs_display_exit;
303*765c9429SGerd Hoffmann     dc->vmsd     = &vmstate_bochs_display;
304*765c9429SGerd Hoffmann     dc->props    = bochs_display_properties;
305*765c9429SGerd Hoffmann     set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
306*765c9429SGerd Hoffmann }
307*765c9429SGerd Hoffmann 
308*765c9429SGerd Hoffmann static const TypeInfo bochs_display_type_info = {
309*765c9429SGerd Hoffmann     .name           = TYPE_BOCHS_DISPLAY,
310*765c9429SGerd Hoffmann     .parent         = TYPE_PCI_DEVICE,
311*765c9429SGerd Hoffmann     .instance_size  = sizeof(BochsDisplayState),
312*765c9429SGerd Hoffmann     .instance_init  = bochs_display_init,
313*765c9429SGerd Hoffmann     .class_init     = bochs_display_class_init,
314*765c9429SGerd Hoffmann     .interfaces     = (InterfaceInfo[]) {
315*765c9429SGerd Hoffmann         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
316*765c9429SGerd Hoffmann         { },
317*765c9429SGerd Hoffmann     },
318*765c9429SGerd Hoffmann };
319*765c9429SGerd Hoffmann 
320*765c9429SGerd Hoffmann static void bochs_display_register_types(void)
321*765c9429SGerd Hoffmann {
322*765c9429SGerd Hoffmann     type_register_static(&bochs_display_type_info);
323*765c9429SGerd Hoffmann }
324*765c9429SGerd Hoffmann 
325*765c9429SGerd Hoffmann type_init(bochs_display_register_types)
326