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 "ui/console.h" 19 #include "sysemu/reset.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; 33 struct RAMFBCfg cfg; 34 bool locked; 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 (linesize == 0) { 54 linesize = width * PIXMAN_FORMAT_BPP(format) / 8; 55 } 56 57 size = (hwaddr)linesize * height; 58 data = cpu_physical_memory_map(addr, &size, false); 59 if (size != (hwaddr)linesize * height) { 60 cpu_physical_memory_unmap(data, size, 0, 0); 61 return NULL; 62 } 63 64 surface = qemu_create_displaysurface_from(width, height, 65 format, linesize, data); 66 pixman_image_set_destroy_function(surface->image, 67 ramfb_unmap_display_surface, NULL); 68 69 return surface; 70 } 71 72 static void ramfb_fw_cfg_write(void *dev, off_t offset, size_t len) 73 { 74 RAMFBState *s = dev; 75 uint32_t fourcc, format, width, height; 76 hwaddr stride, addr; 77 78 width = be32_to_cpu(s->cfg.width); 79 height = be32_to_cpu(s->cfg.height); 80 stride = be32_to_cpu(s->cfg.stride); 81 fourcc = be32_to_cpu(s->cfg.fourcc); 82 addr = be64_to_cpu(s->cfg.addr); 83 format = qemu_drm_format_to_pixman(fourcc); 84 85 fprintf(stderr, "%s: %dx%d @ 0x%" PRIx64 "\n", __func__, 86 width, height, addr); 87 if (s->locked) { 88 fprintf(stderr, "%s: resolution locked, change rejected\n", __func__); 89 return; 90 } 91 s->locked = true; 92 s->width = width; 93 s->height = height; 94 s->ds = ramfb_create_display_surface(s->width, s->height, 95 format, stride, addr); 96 } 97 98 void ramfb_display_update(QemuConsole *con, RAMFBState *s) 99 { 100 if (!s->width || !s->height) { 101 return; 102 } 103 104 if (s->ds) { 105 dpy_gfx_replace_surface(con, s->ds); 106 s->ds = NULL; 107 } 108 109 /* simple full screen update */ 110 dpy_gfx_update_full(con); 111 } 112 113 static void ramfb_reset(void *opaque) 114 { 115 RAMFBState *s = (RAMFBState *)opaque; 116 s->locked = false; 117 memset(&s->cfg, 0, sizeof(s->cfg)); 118 } 119 120 RAMFBState *ramfb_setup(Error **errp) 121 { 122 FWCfgState *fw_cfg = fw_cfg_find(); 123 RAMFBState *s; 124 125 if (!fw_cfg || !fw_cfg->dma_enabled) { 126 error_setg(errp, "ramfb device requires fw_cfg with DMA"); 127 return NULL; 128 } 129 130 s = g_new0(RAMFBState, 1); 131 132 s->locked = false; 133 134 rom_add_vga("vgabios-ramfb.bin"); 135 fw_cfg_add_file_callback(fw_cfg, "etc/ramfb", 136 NULL, ramfb_fw_cfg_write, s, 137 &s->cfg, sizeof(s->cfg), false); 138 qemu_register_reset(ramfb_reset, s); 139 return s; 140 } 141