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