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