2 * early boot framebuffer in guest ram
3 * configured using fw_cfg
5 * Copyright Red Hat, Inc. 2017
8 * Gerd Hoffmann <kraxel@redhat.com>
10 * This work is licensed under the terms of the GNU GPL, version 2 or later.
11 * See the COPYING file in the top-level directory.
14 #include "qemu/osdep.h"
15 #include "qapi/error.h"
16 #include "qemu/option.h"
17 #include "hw/loader.h"
18 #include "hw/display/ramfb.h"
19 #include "ui/console.h"
20 #include "sysemu/reset.h"
22 struct QEMU_PACKED RAMFBCfg
{
33 uint32_t width
, height
;
34 uint32_t starting_width
, starting_height
;
39 static void ramfb_unmap_display_surface(pixman_image_t
*image
, void *unused
)
41 void *data
= pixman_image_get_data(image
);
42 uint32_t size
= pixman_image_get_stride(image
) *
43 pixman_image_get_height(image
);
44 cpu_physical_memory_unmap(data
, size
, 0, 0);
47 static DisplaySurface
*ramfb_create_display_surface(int width
, int height
,
48 pixman_format_code_t format
,
49 int linesize
, uint64_t addr
)
51 DisplaySurface
*surface
;
56 linesize
= width
* PIXMAN_FORMAT_BPP(format
) / 8;
59 size
= (hwaddr
)linesize
* height
;
60 data
= cpu_physical_memory_map(addr
, &size
, 0);
61 if (size
!= (hwaddr
)linesize
* height
) {
62 cpu_physical_memory_unmap(data
, size
, 0, 0);
66 surface
= qemu_create_displaysurface_from(width
, height
,
67 format
, linesize
, data
);
68 pixman_image_set_destroy_function(surface
->image
,
69 ramfb_unmap_display_surface
, NULL
);
74 static void ramfb_fw_cfg_write(void *dev
, off_t offset
, size_t len
)
77 uint32_t fourcc
, format
, width
, height
;
80 width
= be32_to_cpu(s
->cfg
.width
);
81 height
= be32_to_cpu(s
->cfg
.height
);
82 stride
= be32_to_cpu(s
->cfg
.stride
);
83 fourcc
= be32_to_cpu(s
->cfg
.fourcc
);
84 addr
= be64_to_cpu(s
->cfg
.addr
);
85 format
= qemu_drm_format_to_pixman(fourcc
);
87 fprintf(stderr
, "%s: %dx%d @ 0x%" PRIx64
"\n", __func__
,
90 fprintf(stderr
, "%s: resolution locked, change rejected\n", __func__
);
96 s
->ds
= ramfb_create_display_surface(s
->width
, s
->height
,
97 format
, stride
, addr
);
100 void ramfb_display_update(QemuConsole
*con
, RAMFBState
*s
)
102 if (!s
->width
|| !s
->height
) {
107 dpy_gfx_replace_surface(con
, s
->ds
);
111 /* simple full screen update */
112 dpy_gfx_update_full(con
);
115 static void ramfb_reset(void *opaque
)
117 RAMFBState
*s
= (RAMFBState
*)opaque
;
119 memset(&s
->cfg
, 0, sizeof(s
->cfg
));
120 s
->cfg
.width
= s
->starting_width
;
121 s
->cfg
.height
= s
->starting_height
;
124 RAMFBState
*ramfb_setup(DeviceState
* dev
, Error
**errp
)
126 FWCfgState
*fw_cfg
= fw_cfg_find();
129 if (!fw_cfg
|| !fw_cfg
->dma_enabled
) {
130 error_setg(errp
, "ramfb device requires fw_cfg with DMA");
134 s
= g_new0(RAMFBState
, 1);
136 const char *s_fb_width
= qemu_opt_get(dev
->opts
, "xres");
137 const char *s_fb_height
= qemu_opt_get(dev
->opts
, "yres");
139 s
->cfg
.width
= atoi(s_fb_width
);
140 s
->starting_width
= s
->cfg
.width
;
143 s
->cfg
.height
= atoi(s_fb_height
);
144 s
->starting_height
= s
->cfg
.height
;
148 rom_add_vga("vgabios-ramfb.bin");
149 fw_cfg_add_file_callback(fw_cfg
, "etc/ramfb",
150 NULL
, ramfb_fw_cfg_write
, s
,
151 &s
->cfg
, sizeof(s
->cfg
), false);
152 qemu_register_reset(ramfb_reset
, s
);