/* * early boot framebuffer in guest ram * configured using fw_cfg * * Copyright Red Hat, Inc. 2017 * * Author: * Gerd Hoffmann * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/loader.h" #include "hw/display/ramfb.h" #include "hw/display/bochs-vbe.h" /* for limits */ #include "ui/console.h" #include "sysemu/reset.h" struct QEMU_PACKED RAMFBCfg { uint64_t addr; uint32_t fourcc; uint32_t flags; uint32_t width; uint32_t height; uint32_t stride; }; struct RAMFBState { DisplaySurface *ds; uint32_t width, height; struct RAMFBCfg cfg; }; static void ramfb_unmap_display_surface(pixman_image_t *image, void *unused) { void *data = pixman_image_get_data(image); uint32_t size = pixman_image_get_stride(image) * pixman_image_get_height(image); cpu_physical_memory_unmap(data, size, 0, 0); } static DisplaySurface *ramfb_create_display_surface(int width, int height, pixman_format_code_t format, hwaddr stride, hwaddr addr) { DisplaySurface *surface; hwaddr size, mapsize, linesize; void *data; if (width < 16 || width > VBE_DISPI_MAX_XRES || height < 16 || height > VBE_DISPI_MAX_YRES || format == 0 /* unknown format */) return NULL; linesize = width * PIXMAN_FORMAT_BPP(format) / 8; if (stride == 0) { stride = linesize; } mapsize = size = stride * (height - 1) + linesize; data = cpu_physical_memory_map(addr, &mapsize, false); if (size != mapsize) { cpu_physical_memory_unmap(data, mapsize, 0, 0); return NULL; } surface = qemu_create_displaysurface_from(width, height, format, stride, data); pixman_image_set_destroy_function(surface->image, ramfb_unmap_display_surface, NULL); return surface; } static void ramfb_fw_cfg_write(void *dev, off_t offset, size_t len) { RAMFBState *s = dev; DisplaySurface *surface; uint32_t fourcc, format, width, height; hwaddr stride, addr; width = be32_to_cpu(s->cfg.width); height = be32_to_cpu(s->cfg.height); stride = be32_to_cpu(s->cfg.stride); fourcc = be32_to_cpu(s->cfg.fourcc); addr = be64_to_cpu(s->cfg.addr); format = qemu_drm_format_to_pixman(fourcc); surface = ramfb_create_display_surface(width, height, format, stride, addr); if (!surface) { return; } s->width = width; s->height = height; s->ds = surface; } void ramfb_display_update(QemuConsole *con, RAMFBState *s) { if (!s->width || !s->height) { return; } if (s->ds) { dpy_gfx_replace_surface(con, s->ds); s->ds = NULL; } /* simple full screen update */ dpy_gfx_update_full(con); } RAMFBState *ramfb_setup(Error **errp) { FWCfgState *fw_cfg = fw_cfg_find(); RAMFBState *s; if (!fw_cfg || !fw_cfg->dma_enabled) { error_setg(errp, "ramfb device requires fw_cfg with DMA"); return NULL; } s = g_new0(RAMFBState, 1); rom_add_vga("vgabios-ramfb.bin"); fw_cfg_add_file_callback(fw_cfg, "etc/ramfb", NULL, ramfb_fw_cfg_write, s, &s->cfg, sizeof(s->cfg), false); return s; }