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 struct RAMFBState { 32 DisplaySurface *ds; 33 uint32_t width, height; 34 struct RAMFBCfg cfg; 35 }; 36 37 static void ramfb_unmap_display_surface(pixman_image_t *image, void *unused) 38 { 39 void *data = pixman_image_get_data(image); 40 uint32_t size = pixman_image_get_stride(image) * 41 pixman_image_get_height(image); 42 cpu_physical_memory_unmap(data, size, 0, 0); 43 } 44 45 static DisplaySurface *ramfb_create_display_surface(int width, int height, 46 pixman_format_code_t format, 47 hwaddr stride, hwaddr addr) 48 { 49 DisplaySurface *surface; 50 hwaddr size, mapsize, linesize; 51 void *data; 52 53 if (width < 16 || width > VBE_DISPI_MAX_XRES || 54 height < 16 || height > VBE_DISPI_MAX_YRES || 55 format == 0 /* unknown format */) 56 return NULL; 57 58 linesize = width * PIXMAN_FORMAT_BPP(format) / 8; 59 if (stride == 0) { 60 stride = linesize; 61 } 62 63 mapsize = size = stride * (height - 1) + linesize; 64 data = cpu_physical_memory_map(addr, &mapsize, false); 65 if (size != mapsize) { 66 cpu_physical_memory_unmap(data, mapsize, 0, 0); 67 return NULL; 68 } 69 70 surface = qemu_create_displaysurface_from(width, height, 71 format, stride, data); 72 pixman_image_set_destroy_function(surface->image, 73 ramfb_unmap_display_surface, NULL); 74 75 return surface; 76 } 77 78 static void ramfb_fw_cfg_write(void *dev, off_t offset, size_t len) 79 { 80 RAMFBState *s = dev; 81 DisplaySurface *surface; 82 uint32_t fourcc, format, width, height; 83 hwaddr stride, addr; 84 85 width = be32_to_cpu(s->cfg.width); 86 height = be32_to_cpu(s->cfg.height); 87 stride = be32_to_cpu(s->cfg.stride); 88 fourcc = be32_to_cpu(s->cfg.fourcc); 89 addr = be64_to_cpu(s->cfg.addr); 90 format = qemu_drm_format_to_pixman(fourcc); 91 92 surface = ramfb_create_display_surface(width, height, 93 format, stride, addr); 94 if (!surface) { 95 return; 96 } 97 98 s->width = width; 99 s->height = height; 100 s->ds = surface; 101 } 102 103 void ramfb_display_update(QemuConsole *con, RAMFBState *s) 104 { 105 if (!s->width || !s->height) { 106 return; 107 } 108 109 if (s->ds) { 110 dpy_gfx_replace_surface(con, s->ds); 111 s->ds = NULL; 112 } 113 114 /* simple full screen update */ 115 dpy_gfx_update_full(con); 116 } 117 118 RAMFBState *ramfb_setup(Error **errp) 119 { 120 FWCfgState *fw_cfg = fw_cfg_find(); 121 RAMFBState *s; 122 123 if (!fw_cfg || !fw_cfg->dma_enabled) { 124 error_setg(errp, "ramfb device requires fw_cfg with DMA"); 125 return NULL; 126 } 127 128 s = g_new0(RAMFBState, 1); 129 130 rom_add_vga("vgabios-ramfb.bin"); 131 fw_cfg_add_file_callback(fw_cfg, "etc/ramfb", 132 NULL, ramfb_fw_cfg_write, s, 133 &s->cfg, sizeof(s->cfg), false); 134 return s; 135 } 136