qemu ramfb allows to place a boot framebuffer in normal ram. The ramfb vgabios needs a bigger chunk of ram for that, so increase the amout of reserved memory.
Obvious drawback is we waste the memory in case ramfb isn't used. Better ideas are welcome.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/config.h b/src/config.h index 93c8dbc2d5..5912c0dd29 100644 --- a/src/config.h +++ b/src/config.h @@ -17,7 +17,7 @@ // Maximum number of map entries in the e820 map #define BUILD_MAX_E820 32 // Space to reserve in high-memory for tables -#define BUILD_MAX_HIGHTABLE (256*1024) +#define BUILD_MAX_HIGHTABLE (4*1024*1024) // Largest supported externaly facing drive id #define BUILD_MAX_EXTDRIVE 16 // Number of bytes the smbios may be and still live in the f-segment
Also unstatic some qemu_cfg_* functions. This allows ramfb vgabios accessing fw_cfg.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/fw/paravirt.h | 27 +++++++++++++++++++++++++++ src/fw/paravirt.c | 30 +++--------------------------- 2 files changed, 30 insertions(+), 27 deletions(-)
diff --git a/src/fw/paravirt.h b/src/fw/paravirt.h index a14d83e101..ae4440d878 100644 --- a/src/fw/paravirt.h +++ b/src/fw/paravirt.h @@ -10,6 +10,30 @@ #define PF_XEN (1<<1) #define PF_KVM (1<<2)
+// List of QEMU fw_cfg entries. DO NOT ADD MORE. (All new content +// should be passed via the fw_cfg "file" interface.) +#define QEMU_CFG_SIGNATURE 0x00 +#define QEMU_CFG_ID 0x01 +#define QEMU_CFG_UUID 0x02 +#define QEMU_CFG_NOGRAPHIC 0x04 +#define QEMU_CFG_NUMA 0x0d +#define QEMU_CFG_BOOT_MENU 0x0e +#define QEMU_CFG_NB_CPUS 0x05 +#define QEMU_CFG_MAX_CPUS 0x0f +#define QEMU_CFG_FILE_DIR 0x19 +#define QEMU_CFG_ARCH_LOCAL 0x8000 +#define QEMU_CFG_ACPI_TABLES (QEMU_CFG_ARCH_LOCAL + 0) +#define QEMU_CFG_SMBIOS_ENTRIES (QEMU_CFG_ARCH_LOCAL + 1) +#define QEMU_CFG_IRQ0_OVERRIDE (QEMU_CFG_ARCH_LOCAL + 2) +#define QEMU_CFG_E820_TABLE (QEMU_CFG_ARCH_LOCAL + 3) + +struct QemuCfgFile { + u32 size; /* file size */ + u16 select; /* write this to 0x510 to read it */ + u16 reserved; + char name[56]; +}; + typedef struct QemuCfgDmaAccess { u32 control; u32 length; @@ -56,6 +80,9 @@ void qemu_platform_setup(void); void qemu_cfg_init(void);
u16 qemu_get_present_cpus_count(void); +void qemu_cfg_read(void *buf, int len); +void qemu_cfg_read_entry(void *buf, int e, int len); +void qemu_cfg_write_entry(void *buf, int e, int len); int qemu_cfg_write_file(void *src, struct romfile_s *file, u32 offset, u32 len); int qemu_cfg_write_file_simple(void *src, u16 key, u32 offset, u32 len); u16 qemu_get_romfile_key(struct romfile_s *file); diff --git a/src/fw/paravirt.c b/src/fw/paravirt.c index 0770c47b12..4cfaec220f 100644 --- a/src/fw/paravirt.c +++ b/src/fw/paravirt.c @@ -206,23 +206,6 @@ qemu_platform_setup(void) * QEMU firmware config (fw_cfg) interface ****************************************************************/
-// List of QEMU fw_cfg entries. DO NOT ADD MORE. (All new content -// should be passed via the fw_cfg "file" interface.) -#define QEMU_CFG_SIGNATURE 0x00 -#define QEMU_CFG_ID 0x01 -#define QEMU_CFG_UUID 0x02 -#define QEMU_CFG_NOGRAPHIC 0x04 -#define QEMU_CFG_NUMA 0x0d -#define QEMU_CFG_BOOT_MENU 0x0e -#define QEMU_CFG_NB_CPUS 0x05 -#define QEMU_CFG_MAX_CPUS 0x0f -#define QEMU_CFG_FILE_DIR 0x19 -#define QEMU_CFG_ARCH_LOCAL 0x8000 -#define QEMU_CFG_ACPI_TABLES (QEMU_CFG_ARCH_LOCAL + 0) -#define QEMU_CFG_SMBIOS_ENTRIES (QEMU_CFG_ARCH_LOCAL + 1) -#define QEMU_CFG_IRQ0_OVERRIDE (QEMU_CFG_ARCH_LOCAL + 2) -#define QEMU_CFG_E820_TABLE (QEMU_CFG_ARCH_LOCAL + 3) - static void qemu_cfg_select(u16 f) { @@ -247,7 +230,7 @@ qemu_cfg_dma_transfer(void *address, u32 length, u32 control) } }
-static void +void qemu_cfg_read(void *buf, int len) { if (len == 0) { @@ -290,7 +273,7 @@ qemu_cfg_skip(int len) } }
-static void +void qemu_cfg_read_entry(void *buf, int e, int len) { if (qemu_cfg_dma_enabled()) { @@ -303,7 +286,7 @@ qemu_cfg_read_entry(void *buf, int e, int len) } }
-static void +void qemu_cfg_write_entry(void *buf, int e, int len) { if (qemu_cfg_dma_enabled()) { @@ -561,13 +544,6 @@ qemu_cfg_legacy(void) } }
-struct QemuCfgFile { - u32 size; /* file size */ - u16 select; /* write this to 0x510 to read it */ - u16 reserved; - char name[56]; -}; - void qemu_cfg_init(void) { if (!runningOnQEMU())
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
On Wed, Jun 13, 2018 at 10:51:55AM +0200, Gerd Hoffmann wrote:
qemu ramfb allows to place a boot framebuffer in normal ram. The ramfb vgabios needs a bigger chunk of ram for that, so increase the amout of reserved memory.
Obvious drawback is we waste the memory in case ramfb isn't used. Better ideas are welcome.
One possibility would be to detect large permanent high memory allocations in the SeaBIOS PMM interface and have it directly allocate them from the e820 map. It would be a bit of a hack though - not sure which is better.
-Kevin