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