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/loader.h" 17 #include "hw/display/ramfb.h" 18 #include "hw/display/bochs-vbe.h" /* for limits */ 19 #include "ui/console.h" 20 #include "sysemu/reset.h" 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 31 typedef struct RAMFBCfg RAMFBCfg; 32 33 struct RAMFBState { 34 DisplaySurface *ds; 35 uint32_t width, height; 36 struct RAMFBCfg cfg; 37 }; 38 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, 49 hwaddr stride, hwaddr addr) 50 { 51 DisplaySurface *surface; 52 hwaddr size, mapsize, linesize; 53 void *data; 54 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 60 linesize = width * PIXMAN_FORMAT_BPP(format) / 8; 61 if (stride == 0) { 62 stride = linesize; 63 } 64 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); 69 return NULL; 70 } 71 72 surface = qemu_create_displaysurface_from(width, height, 73 format, stride, data); 74 pixman_image_set_destroy_function(surface->image, 75 ramfb_unmap_display_surface, NULL); 76 77 return surface; 78 } 79 80 static void ramfb_fw_cfg_write(void *dev, off_t offset, size_t len) 81 { 82 RAMFBState *s = dev; 83 DisplaySurface *surface; 84 uint32_t fourcc, format, width, height; 85 hwaddr stride, addr; 86 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 } 99 100 s->width = width; 101 s->height = height; 102 qemu_free_displaysurface(s->ds); 103 s->ds = surface; 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 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 138 RAMFBState *ramfb_setup(Error **errp) 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 150 rom_add_vga("vgabios-ramfb.bin"); 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 } 156