]>
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 | */ | |
13 | #include "qemu/osdep.h" | |
14 | #include "qapi/error.h" | |
f79081b4 | 15 | #include "qemu/option.h" |
995b3017 GH |
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; | |
f79081b4 | 33 | uint32_t starting_width, starting_height; |
995b3017 | 34 | struct RAMFBCfg cfg; |
a9e0cb67 | 35 | bool locked; |
995b3017 GH |
36 | }; |
37 | ||
d57f252a HQ |
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 | ||
995b3017 GH |
73 | static void ramfb_fw_cfg_write(void *dev, off_t offset, size_t len) |
74 | { | |
75 | RAMFBState *s = dev; | |
a9e0cb67 | 76 | uint32_t fourcc, format, width, height; |
d57f252a | 77 | hwaddr stride, addr; |
995b3017 | 78 | |
a9e0cb67 HQ |
79 | width = be32_to_cpu(s->cfg.width); |
80 | height = be32_to_cpu(s->cfg.height); | |
995b3017 GH |
81 | stride = be32_to_cpu(s->cfg.stride); |
82 | fourcc = be32_to_cpu(s->cfg.fourcc); | |
83 | addr = be64_to_cpu(s->cfg.addr); | |
995b3017 GH |
84 | format = qemu_drm_format_to_pixman(fourcc); |
85 | ||
86 | fprintf(stderr, "%s: %dx%d @ 0x%" PRIx64 "\n", __func__, | |
a9e0cb67 HQ |
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; | |
d57f252a HQ |
95 | s->ds = ramfb_create_display_surface(s->width, s->height, |
96 | format, stride, addr); | |
995b3017 GH |
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 | ||
a9e0cb67 HQ |
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)); | |
f79081b4 HQ |
119 | s->cfg.width = s->starting_width; |
120 | s->cfg.height = s->starting_height; | |
a9e0cb67 HQ |
121 | } |
122 | ||
f79081b4 | 123 | RAMFBState *ramfb_setup(DeviceState* dev, Error **errp) |
995b3017 GH |
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 | ||
f79081b4 HQ |
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 | } | |
a9e0cb67 HQ |
145 | s->locked = false; |
146 | ||
9f5d9c19 | 147 | rom_add_vga("vgabios-ramfb.bin"); |
995b3017 GH |
148 | fw_cfg_add_file_callback(fw_cfg, "etc/ramfb", |
149 | NULL, ramfb_fw_cfg_write, s, | |
150 | &s->cfg, sizeof(s->cfg), false); | |
a9e0cb67 | 151 | qemu_register_reset(ramfb_reset, s); |
995b3017 GH |
152 | return s; |
153 | } |