Add support for qemu ramfb. This is a simple boot framebuffer device, with normal ram being used to back the framebuffer and fw_cfg being used to configure the device.
Use case (on x86): boot display for vgpu devices (which neither emulate vga nor have a vgabios).
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- Makefile | 2 +- vgasrc/vgahw.h | 28 +++++++------ vgasrc/vgautil.h | 3 ++ vgasrc/ramfb.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ vgasrc/Kconfig | 7 ++++ 5 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 vgasrc/ramfb.c
diff --git a/Makefile b/Makefile index de2a145c39..e13a60e647 100644 --- a/Makefile +++ b/Makefile @@ -213,7 +213,7 @@ SRCVGA=src/output.c src/string.c src/hw/pci.c src/hw/serialio.c \ vgasrc/vgafonts.c vgasrc/vbe.c \ vgasrc/stdvga.c vgasrc/stdvgamodes.c vgasrc/stdvgaio.c \ vgasrc/clext.c vgasrc/bochsvga.c vgasrc/geodevga.c \ - src/fw/coreboot.c vgasrc/cbvga.c vgasrc/bochsdisplay.c + src/fw/coreboot.c src/fw/paravirt.c vgasrc/cbvga.c vgasrc/bochsdisplay.c vgasrc/ramfb.c
ifeq "$(CONFIG_VGA_FIXUP_ASM)" "y" $(OUT)vgaccode16.raw.s: $(OUT)autoconf.h $(patsubst %.c, $(OUT)%.o,$(SRCVGA)) ; $(call whole-compile, $(filter-out -fomit-frame-pointer,$(CFLAGS16)) -fno-omit-frame-pointer -S -Isrc, $(SRCVGA),$@) diff --git a/vgasrc/vgahw.h b/vgasrc/vgahw.h index 6d6ff1aa7b..51777458da 100644 --- a/vgasrc/vgahw.h +++ b/vgasrc/vgahw.h @@ -14,7 +14,7 @@ static inline struct vgamode_s *vgahw_find_mode(int mode) { return clext_find_mode(mode); if (CONFIG_VGA_BOCHS) return bochsvga_find_mode(mode); - if (CONFIG_VGA_COREBOOT || CONFIG_DISPLAY_BOCHS) + if (CONFIG_VGA_EMULATE_TEXT) return cbvga_find_mode(mode); return stdvga_find_mode(mode); } @@ -24,7 +24,7 @@ static inline int vgahw_set_mode(struct vgamode_s *vmode_g, int flags) { return clext_set_mode(vmode_g, flags); if (CONFIG_VGA_BOCHS) return bochsvga_set_mode(vmode_g, flags); - if (CONFIG_VGA_COREBOOT || CONFIG_DISPLAY_BOCHS) + if (CONFIG_VGA_EMULATE_TEXT) return cbvga_set_mode(vmode_g, flags); return stdvga_set_mode(vmode_g, flags); } @@ -34,7 +34,7 @@ static inline void vgahw_list_modes(u16 seg, u16 *dest, u16 *last) { clext_list_modes(seg, dest, last); else if (CONFIG_VGA_BOCHS) bochsvga_list_modes(seg, dest, last); - else if (CONFIG_VGA_COREBOOT || CONFIG_DISPLAY_BOCHS) + else if (CONFIG_VGA_EMULATE_TEXT) cbvga_list_modes(seg, dest, last); else stdvga_list_modes(seg, dest, last); @@ -51,6 +51,8 @@ static inline int vgahw_setup(void) { return cbvga_setup(); if (CONFIG_DISPLAY_BOCHS) return bochs_display_setup(); + if (CONFIG_VGA_RAMFB) + return ramfb_setup(); return stdvga_setup(); }
@@ -59,7 +61,7 @@ static inline int vgahw_get_window(struct vgamode_s *vmode_g, int window) { return clext_get_window(vmode_g, window); if (CONFIG_VGA_BOCHS) return bochsvga_get_window(vmode_g, window); - if (CONFIG_VGA_COREBOOT || CONFIG_DISPLAY_BOCHS) + if (CONFIG_VGA_EMULATE_TEXT) return cbvga_get_window(vmode_g, window); return stdvga_get_window(vmode_g, window); } @@ -70,7 +72,7 @@ static inline int vgahw_set_window(struct vgamode_s *vmode_g, int window return clext_set_window(vmode_g, window, val); if (CONFIG_VGA_BOCHS) return bochsvga_set_window(vmode_g, window, val); - if (CONFIG_VGA_COREBOOT || CONFIG_DISPLAY_BOCHS) + if (CONFIG_VGA_EMULATE_TEXT) return cbvga_set_window(vmode_g, window, val); return stdvga_set_window(vmode_g, window, val); } @@ -80,7 +82,7 @@ static inline int vgahw_get_linelength(struct vgamode_s *vmode_g) { return clext_get_linelength(vmode_g); if (CONFIG_VGA_BOCHS) return bochsvga_get_linelength(vmode_g); - if (CONFIG_VGA_COREBOOT || CONFIG_DISPLAY_BOCHS) + if (CONFIG_VGA_EMULATE_TEXT) return cbvga_get_linelength(vmode_g); return stdvga_get_linelength(vmode_g); } @@ -90,7 +92,7 @@ static inline int vgahw_set_linelength(struct vgamode_s *vmode_g, int val) { return clext_set_linelength(vmode_g, val); if (CONFIG_VGA_BOCHS) return bochsvga_set_linelength(vmode_g, val); - if (CONFIG_VGA_COREBOOT || CONFIG_DISPLAY_BOCHS) + if (CONFIG_VGA_EMULATE_TEXT) return cbvga_set_linelength(vmode_g, val); return stdvga_set_linelength(vmode_g, val); } @@ -100,7 +102,7 @@ static inline int vgahw_get_displaystart(struct vgamode_s *vmode_g) { return clext_get_displaystart(vmode_g); if (CONFIG_VGA_BOCHS) return bochsvga_get_displaystart(vmode_g); - if (CONFIG_VGA_COREBOOT || CONFIG_DISPLAY_BOCHS) + if (CONFIG_VGA_EMULATE_TEXT) return cbvga_get_displaystart(vmode_g); return stdvga_get_displaystart(vmode_g); } @@ -110,7 +112,7 @@ static inline int vgahw_set_displaystart(struct vgamode_s *vmode_g, int val) { return clext_set_displaystart(vmode_g, val); if (CONFIG_VGA_BOCHS) return bochsvga_set_displaystart(vmode_g, val); - if (CONFIG_VGA_COREBOOT || CONFIG_DISPLAY_BOCHS) + if (CONFIG_VGA_EMULATE_TEXT) return cbvga_set_displaystart(vmode_g, val); return stdvga_set_displaystart(vmode_g, val); } @@ -118,7 +120,7 @@ static inline int vgahw_set_displaystart(struct vgamode_s *vmode_g, int val) { static inline int vgahw_get_dacformat(struct vgamode_s *vmode_g) { if (CONFIG_VGA_BOCHS) return bochsvga_get_dacformat(vmode_g); - if (CONFIG_VGA_COREBOOT || CONFIG_DISPLAY_BOCHS) + if (CONFIG_VGA_EMULATE_TEXT) return cbvga_get_dacformat(vmode_g); return stdvga_get_dacformat(vmode_g); } @@ -126,7 +128,7 @@ static inline int vgahw_get_dacformat(struct vgamode_s *vmode_g) { static inline int vgahw_set_dacformat(struct vgamode_s *vmode_g, int val) { if (CONFIG_VGA_BOCHS) return bochsvga_set_dacformat(vmode_g, val); - if (CONFIG_VGA_COREBOOT || CONFIG_DISPLAY_BOCHS) + if (CONFIG_VGA_EMULATE_TEXT) return cbvga_set_dacformat(vmode_g, val); return stdvga_set_dacformat(vmode_g, val); } @@ -136,13 +138,13 @@ static inline int vgahw_save_restore(int cmd, u16 seg, void *data) { return clext_save_restore(cmd, seg, data); if (CONFIG_VGA_BOCHS) return bochsvga_save_restore(cmd, seg, data); - if (CONFIG_VGA_COREBOOT || CONFIG_DISPLAY_BOCHS) + if (CONFIG_VGA_EMULATE_TEXT) return cbvga_save_restore(cmd, seg, data); return stdvga_save_restore(cmd, seg, data); }
static inline int vgahw_get_linesize(struct vgamode_s *vmode_g) { - if (CONFIG_VGA_COREBOOT || CONFIG_DISPLAY_BOCHS) + if (CONFIG_VGA_EMULATE_TEXT) return cbvga_get_linesize(vmode_g); return stdvga_get_linesize(vmode_g); } diff --git a/vgasrc/vgautil.h b/vgasrc/vgautil.h index d93da76b4a..9d969bbe46 100644 --- a/vgasrc/vgautil.h +++ b/vgasrc/vgautil.h @@ -24,6 +24,9 @@ int cbvga_setup(void); // bochsdisplay.c int bochs_display_setup(void);
+// ramfb.c +int ramfb_setup(void); + // clext.c struct vgamode_s *clext_find_mode(int mode); void clext_list_modes(u16 seg, u16 *dest, u16 *last); diff --git a/vgasrc/ramfb.c b/vgasrc/ramfb.c new file mode 100644 index 0000000000..30b8cebaa6 --- /dev/null +++ b/vgasrc/ramfb.c @@ -0,0 +1,123 @@ +#include "biosvar.h" // GET_BDA +#include "output.h" // dprintf +#include "string.h" // memset16_far +#include "vgautil.h" // VBE_total_memory +#include "std/pmm.h" // struct pmmheader +#include "byteorder.h" +#include "fw/paravirt.h" + +#define FRAMEBUFFER_WIDTH 1024 +#define FRAMEBUFFER_HEIGHT 768 +#define FRAMEBUFFER_BPP 4 +#define FRAMEBUFFER_STRIDE (FRAMEBUFFER_BPP * FRAMEBUFFER_WIDTH) +#define FRAMEBUFFER_SIZE (FRAMEBUFFER_STRIDE * FRAMEBUFFER_HEIGHT) + +struct QemuRAMFBCfg { + u64 addr; + u32 fourcc; + u32 flags; + u32 width; + u32 height; + u32 stride; +}; + +#define fourcc_code(a, b, c, d) ((u32)(a) | ((u32)(b) << 8) | \ + ((u32)(c) << 16) | ((u32)(d) << 24)) + +#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */ +#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */ +#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */ + +static u32 +allocate_framebuffer(void) +{ + u32 pmmscan; + for (pmmscan=0; pmmscan < BUILD_BIOS_SIZE; pmmscan+=16) { + struct pmmheader *pmm = (void*)pmmscan; + if (GET_FARVAR(SEG_BIOS, pmm->signature) != PMM_SIGNATURE) + continue; + if (checksum_far(SEG_BIOS, pmm, GET_FARVAR(SEG_BIOS, pmm->length))) + continue; + struct segoff_s entry = GET_FARVAR(SEG_BIOS, pmm->entry); + dprintf(1, "ramfb: alloc framebuffer via pmm call to %04x:%04x\n" + , entry.seg, entry.offset); + u16 res1, res2; + asm volatile( + "pushl %0\n" + "pushw $(8|2)\n" // Permanent high memory request + "pushl $0xffffffff\n" // Anonymous handle + "pushl $" __stringify(FRAMEBUFFER_SIZE / 16) "\n" + "pushw $0x00\n" // PMM allocation request + "lcallw *12(%%esp)\n" + "addl $16, %%esp\n" + "cli\n" + "cld\n" + : "+r" (entry.segoff), "=a" (res1), "=d" (res2) : : "cc", "memory"); + u32 res = res1 | (res2 << 16); + if (!res || res == PMM_FUNCTION_NOT_SUPPORTED) + return 0; + dprintf(1, "ramfb: framebuffer allocated at %x\n", res); + return res; + } + return 0; +} + +int +ramfb_setup(void) +{ + dprintf(1, "ramfb: init\n"); + + if (GET_GLOBAL(HaveRunInit)) + return 0; + + u32 count; + qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count)); + count = be32_to_cpu(count); + u32 select, e, fb; + for (select = 0, e = 0; e < count; e++) { + struct QemuCfgFile qfile; + qemu_cfg_read(&qfile, sizeof(qfile)); + if (memcmp_far(GET_SEG(SS), qfile.name, + GET_SEG(CS), "etc/ramfb", 10) == 0) + select = be16_to_cpu(qfile.select); + } + if (select == 0) { + dprintf(1, "ramfb: fw_cfg (etc/ramfb) file not found\n"); + return -1; + } + + dprintf(1, "ramfb: fw_cfg (etc/ramfb) file at slot 0x%x\n", select); + fb = allocate_framebuffer(); + if (!fb) { + dprintf(1, "ramfb: allocating framebuffer failed\n"); + return -1; + } + + u64 addr = fb; + u8 bpp = FRAMEBUFFER_BPP * 8; + u32 xlines = FRAMEBUFFER_WIDTH; + u32 ylines = FRAMEBUFFER_HEIGHT; + u32 linelength = FRAMEBUFFER_STRIDE; + dprintf(1, "Found FB @ %llx %dx%d with %d bpp (%d stride)\n" + , addr, xlines, ylines, bpp, linelength); + + if (!addr || addr > 0xffffffff + || (bpp != 15 && bpp != 16 && bpp != 24 && bpp != 32)) { + dprintf(1, "Unable to use FB\n"); + return -1; + } + + cbvga_setup_modes(addr, bpp, xlines, ylines, linelength); + + struct QemuRAMFBCfg cfg = { + .addr = cpu_to_be64(fb), + .fourcc = cpu_to_be32(DRM_FORMAT_XRGB8888), + .flags = cpu_to_be32(0), + .width = cpu_to_be32(FRAMEBUFFER_WIDTH), + .height = cpu_to_be32(FRAMEBUFFER_HEIGHT), + .stride = cpu_to_be32(FRAMEBUFFER_STRIDE), + }; + qemu_cfg_write_entry(&cfg, select, sizeof(cfg)); + + return 0; +} diff --git a/vgasrc/Kconfig b/vgasrc/Kconfig index 4443c0b37a..1933b0e23f 100644 --- a/vgasrc/Kconfig +++ b/vgasrc/Kconfig @@ -70,6 +70,13 @@ menu "VGA ROM" v3.0+. The vgabios works with the qemu stdvga too (use "qemu -device VGA,romfile=/path/to/vgabios.bin")".
+ config VGA_RAMFB + depends on QEMU + bool "qemu ram framebuffer (experimental)" + select VGA_EMULATE_TEXT + help + qemu ram framebuffer (experimental) + endchoice
choice