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