| /* |
| * early boot framebuffer in guest ram |
| * configured using fw_cfg |
| * |
| * Copyright Red Hat, Inc. 2017 |
| * |
| * Author: |
| * Gerd Hoffmann <kraxel@redhat.com> |
| * |
| * 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 "qemu/option.h" |
| #include "hw/loader.h" |
| #include "hw/display/ramfb.h" |
| #include "ui/console.h" |
| #include "sysemu/sysemu.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; |
| uint32_t starting_width, starting_height; |
| struct RAMFBCfg cfg; |
| bool locked; |
| }; |
| |
| 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, |
| int linesize, uint64_t addr) |
| { |
| DisplaySurface *surface; |
| hwaddr size; |
| void *data; |
| |
| if (linesize == 0) { |
| linesize = width * PIXMAN_FORMAT_BPP(format) / 8; |
| } |
| |
| size = (hwaddr)linesize * height; |
| data = cpu_physical_memory_map(addr, &size, 0); |
| if (size != (hwaddr)linesize * height) { |
| cpu_physical_memory_unmap(data, size, 0, 0); |
| return NULL; |
| } |
| |
| surface = qemu_create_displaysurface_from(width, height, |
| format, linesize, 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; |
| 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); |
| |
| fprintf(stderr, "%s: %dx%d @ 0x%" PRIx64 "\n", __func__, |
| width, height, addr); |
| if (s->locked) { |
| fprintf(stderr, "%s: resolution locked, change rejected\n", __func__); |
| return; |
| } |
| s->locked = true; |
| s->width = width; |
| s->height = height; |
| s->ds = ramfb_create_display_surface(s->width, s->height, |
| format, stride, addr); |
| } |
| |
| 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); |
| } |
| |
| static void ramfb_reset(void *opaque) |
| { |
| RAMFBState *s = (RAMFBState *)opaque; |
| s->locked = false; |
| memset(&s->cfg, 0, sizeof(s->cfg)); |
| s->cfg.width = s->starting_width; |
| s->cfg.height = s->starting_height; |
| } |
| |
| RAMFBState *ramfb_setup(DeviceState* dev, 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); |
| |
| const char *s_fb_width = qemu_opt_get(dev->opts, "xres"); |
| const char *s_fb_height = qemu_opt_get(dev->opts, "yres"); |
| if (s_fb_width) { |
| s->cfg.width = atoi(s_fb_width); |
| s->starting_width = s->cfg.width; |
| } |
| if (s_fb_height) { |
| s->cfg.height = atoi(s_fb_height); |
| s->starting_height = s->cfg.height; |
| } |
| s->locked = false; |
| |
| 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); |
| qemu_register_reset(ramfb_reset, s); |
| return s; |
| } |