xref: /openbmc/qemu/hw/display/ramfb.c (revision 64552b6b)
1 /*
2  * early boot framebuffer in guest ram
3  * configured using fw_cfg
4  *
5  * Copyright Red Hat, Inc. 2017
6  *
7  * Author:
8  *     Gerd Hoffmann <kraxel@redhat.com>
9  *
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.
12  */
13 
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"
21 #include "sysemu/sysemu.h"
22 
23 struct QEMU_PACKED RAMFBCfg {
24     uint64_t addr;
25     uint32_t fourcc;
26     uint32_t flags;
27     uint32_t width;
28     uint32_t height;
29     uint32_t stride;
30 };
31 
32 struct RAMFBState {
33     DisplaySurface *ds;
34     uint32_t width, height;
35     uint32_t starting_width, starting_height;
36     struct RAMFBCfg cfg;
37     bool locked;
38 };
39 
40 static void ramfb_unmap_display_surface(pixman_image_t *image, void *unused)
41 {
42     void *data = pixman_image_get_data(image);
43     uint32_t size = pixman_image_get_stride(image) *
44         pixman_image_get_height(image);
45     cpu_physical_memory_unmap(data, size, 0, 0);
46 }
47 
48 static DisplaySurface *ramfb_create_display_surface(int width, int height,
49                                                     pixman_format_code_t format,
50                                                     int linesize, uint64_t addr)
51 {
52     DisplaySurface *surface;
53     hwaddr size;
54     void *data;
55 
56     if (linesize == 0) {
57         linesize = width * PIXMAN_FORMAT_BPP(format) / 8;
58     }
59 
60     size = (hwaddr)linesize * height;
61     data = cpu_physical_memory_map(addr, &size, 0);
62     if (size != (hwaddr)linesize * height) {
63         cpu_physical_memory_unmap(data, size, 0, 0);
64         return NULL;
65     }
66 
67     surface = qemu_create_displaysurface_from(width, height,
68                                               format, linesize, data);
69     pixman_image_set_destroy_function(surface->image,
70                                       ramfb_unmap_display_surface, NULL);
71 
72     return surface;
73 }
74 
75 static void ramfb_fw_cfg_write(void *dev, off_t offset, size_t len)
76 {
77     RAMFBState *s = dev;
78     uint32_t fourcc, format, width, height;
79     hwaddr stride, addr;
80 
81     width     = be32_to_cpu(s->cfg.width);
82     height    = be32_to_cpu(s->cfg.height);
83     stride    = be32_to_cpu(s->cfg.stride);
84     fourcc    = be32_to_cpu(s->cfg.fourcc);
85     addr      = be64_to_cpu(s->cfg.addr);
86     format    = qemu_drm_format_to_pixman(fourcc);
87 
88     fprintf(stderr, "%s: %dx%d @ 0x%" PRIx64 "\n", __func__,
89             width, height, addr);
90     if (s->locked) {
91         fprintf(stderr, "%s: resolution locked, change rejected\n", __func__);
92         return;
93     }
94     s->locked = true;
95     s->width = width;
96     s->height = height;
97     s->ds = ramfb_create_display_surface(s->width, s->height,
98                                          format, stride, addr);
99 }
100 
101 void ramfb_display_update(QemuConsole *con, RAMFBState *s)
102 {
103     if (!s->width || !s->height) {
104         return;
105     }
106 
107     if (s->ds) {
108         dpy_gfx_replace_surface(con, s->ds);
109         s->ds = NULL;
110     }
111 
112     /* simple full screen update */
113     dpy_gfx_update_full(con);
114 }
115 
116 static void ramfb_reset(void *opaque)
117 {
118     RAMFBState *s = (RAMFBState *)opaque;
119     s->locked = false;
120     memset(&s->cfg, 0, sizeof(s->cfg));
121     s->cfg.width = s->starting_width;
122     s->cfg.height = s->starting_height;
123 }
124 
125 RAMFBState *ramfb_setup(DeviceState* dev, Error **errp)
126 {
127     FWCfgState *fw_cfg = fw_cfg_find();
128     RAMFBState *s;
129 
130     if (!fw_cfg || !fw_cfg->dma_enabled) {
131         error_setg(errp, "ramfb device requires fw_cfg with DMA");
132         return NULL;
133     }
134 
135     s = g_new0(RAMFBState, 1);
136 
137     const char *s_fb_width = qemu_opt_get(dev->opts, "xres");
138     const char *s_fb_height = qemu_opt_get(dev->opts, "yres");
139     if (s_fb_width) {
140         s->cfg.width = atoi(s_fb_width);
141         s->starting_width = s->cfg.width;
142     }
143     if (s_fb_height) {
144         s->cfg.height = atoi(s_fb_height);
145         s->starting_height = s->cfg.height;
146     }
147     s->locked = false;
148 
149     rom_add_vga("vgabios-ramfb.bin");
150     fw_cfg_add_file_callback(fw_cfg, "etc/ramfb",
151                              NULL, ramfb_fw_cfg_write, s,
152                              &s->cfg, sizeof(s->cfg), false);
153     qemu_register_reset(ramfb_reset, s);
154     return s;
155 }
156