master
c 159 lines 4.07 KB
Raw
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 "hw/core/loader.h"
17 #include "hw/display/ramfb.h"
18 #include "hw/display/bochs-vbe.h" /* for limits */
19 #include "ui/console.h"
20 #include "system/physmem.h"
21 #include "system/reset.h"
22 #include "exec/cpu-common.h"
23
24 struct QEMU_PACKED RAMFBCfg {
25 uint64_t addr;
26 uint32_t fourcc;
27 uint32_t flags;
28 uint32_t width;
29 uint32_t height;
30 uint32_t stride;
31 };
32
33 typedef struct RAMFBCfg RAMFBCfg;
34
35 struct RAMFBState {
36 DisplaySurface *ds;
37 uint32_t width, height;
38 struct RAMFBCfg cfg;
39 };
40
41 static void ramfb_unmap_display_surface(pixman_image_t *image, void *unused)
42 {
43 void *data = pixman_image_get_data(image);
44 uint32_t size = pixman_image_get_stride(image) *
45 pixman_image_get_height(image);
46 physical_memory_unmap(data, size, 0, 0);
47 }
48
49 static DisplaySurface *ramfb_create_display_surface(int width, int height,
50 pixman_format_code_t format,
51 hwaddr stride, hwaddr addr)
52 {
53 DisplaySurface *surface;
54 hwaddr size, mapsize, linesize;
55 void *data;
56
57 if (width < 16 || width > VBE_DISPI_MAX_XRES ||
58 height < 16 || height > VBE_DISPI_MAX_YRES ||
59 format == 0 /* unknown format */)
60 return NULL;
61
62 linesize = width * PIXMAN_FORMAT_BPP(format) / 8;
63 if (stride == 0) {
64 stride = linesize;
65 }
66
67 mapsize = size = stride * (height - 1) + linesize;
68 data = physical_memory_map(addr, &mapsize, false);
69 if (size != mapsize) {
70 physical_memory_unmap(data, mapsize, 0, 0);
71 return NULL;
72 }
73
74 surface = qemu_create_displaysurface_from(width, height,
75 format, stride, data);
76 pixman_image_set_destroy_function(surface->image,
77 ramfb_unmap_display_surface, NULL);
78
79 return surface;
80 }
81
82 static void ramfb_fw_cfg_write(void *dev, off_t offset, size_t len)
83 {
84 RAMFBState *s = dev;
85 DisplaySurface *surface;
86 uint32_t fourcc, format, width, height;
87 hwaddr stride, addr;
88
89 width = be32_to_cpu(s->cfg.width);
90 height = be32_to_cpu(s->cfg.height);
91 stride = be32_to_cpu(s->cfg.stride);
92 fourcc = be32_to_cpu(s->cfg.fourcc);
93 addr = be64_to_cpu(s->cfg.addr);
94 format = qemu_drm_format_to_pixman(fourcc);
95
96 surface = ramfb_create_display_surface(width, height,
97 format, stride, addr);
98 if (!surface) {
99 return;
100 }
101
102 s->width = width;
103 s->height = height;
104 qemu_free_displaysurface(s->ds);
105 s->ds = surface;
106 }
107
108 void ramfb_display_update(QemuConsole *con, RAMFBState *s)
109 {
110 if (!s->width || !s->height) {
111 return;
112 }
113
114 if (s->ds) {
115 qemu_console_set_surface(con, s->ds);
116 s->ds = NULL;
117 }
118
119 /* simple full screen update */
120 qemu_console_update_full(con);
121 }
122
123 static int ramfb_post_load(void *opaque, int version_id)
124 {
125 ramfb_fw_cfg_write(opaque, 0, 0);
126 return 0;
127 }
128
129 const VMStateDescription ramfb_vmstate = {
130 .name = "ramfb",
131 .version_id = 1,
132 .minimum_version_id = 1,
133 .post_load = ramfb_post_load,
134 .fields = (const VMStateField[]) {
135 VMSTATE_BUFFER_UNSAFE(cfg, RAMFBState, 0, sizeof(RAMFBCfg)),
136 VMSTATE_END_OF_LIST()
137 }
138 };
139
140 RAMFBState *ramfb_setup(bool romfile, Error **errp)
141 {
142 FWCfgState *fw_cfg = fw_cfg_find();
143 RAMFBState *s;
144
145 if (!fw_cfg || !fw_cfg->dma_enabled) {
146 error_setg(errp, "ramfb device requires fw_cfg with DMA");
147 return NULL;
148 }
149
150 s = g_new0(RAMFBState, 1);
151
152 if (romfile) {
153 rom_add_vga("vgabios-ramfb.bin");
154 }
155 fw_cfg_add_file_callback(fw_cfg, "etc/ramfb",
156 NULL, ramfb_fw_cfg_write, s,
157 &s->cfg, sizeof(s->cfg), false);
158 return s;
159 }