[SeaBIOS] Reducing SeaBIOS kernel entry time

Kevin O'Connor kevin at koconnor.net
Thu Jul 9 00:57:26 CEST 2015


On Wed, Jul 08, 2015 at 03:44:33PM +0200, Marc Marí wrote:
> On Wed, 8 Jul 2015 09:10:14 +0100
> Stefan Hajnoczi <stefanha at gmail.com> wrote:
> 
> > On Sat, Jul 4, 2015 at 6:57 PM, Kevin O'Connor <kevin at koconnor.net>
> > wrote:
> > > On Fri, Jul 03, 2015 at 03:12:14PM +0100, Stefan Hajnoczi wrote:
> > >> On Fri, Jul 3, 2015 at 2:13 PM, Kevin O'Connor
> > >> <kevin at koconnor.net> wrote:
> > >> > I took a look a month or so ago:
> > >> >
> > >> > http://article.gmane.org/gmane.comp.emulators.kvm.devel/136207
> > >> >
> > >> > Last test I ran I had SeaBIOS down to 16ms (not including the
> > >> > time to deploy or jump to the linux kernel).  More reductions
> > >> > are also possible - the above was just with Kconfig settings.
> > >> >
> 
> I've been measuring the time for different configurations and setups.
> 
> The conclusion is that the main bottleneck to boot QEMU is fw_cfg.
> 
> Running a default SeaBIOS configuration gives these times (in seconds):
> 
> (QEMU startup time is the time between the execution and the first
> kvm_entry, BIOS startup time is the time between the first kvm_entry
> and the ROM booting, and fw_cfg is the time between the ROM booting
> and the jump to the Linux kernel).
> 
> QEMU startup time: .033
> BIOS startup time: .154
> fw_cfg setup time: .375
> 
> And these results are more or less the same in QBoot, when using fw_cfg:
> 
> QEMU startup time: .026
> BIOS startup time: .013
> fw_cfg setup time: .373
> 
> The difference between SeaBIOS and QBoot is big, but, as I said, this
> SeaBIOS is not stripped-down. Using the .config that Kevin sent, I can
> get .01 seconds, but I cannot boot a ROM (I still have to play with the
> configuration options until it boots). Probably it can do it in less
> than 20 msec, which not far from QBoot.
> 
> On the other side, QBoot with cbfs can boot really fast:
> 
> QEMU startup time: .027
> Kernel setup time: .017
> Total time: .045

Thanks.  Can you try the latest seabios with the patch below and the
attached config?  The patch is a complete hack, but with it you should
be able to launch seabios with the same cbfs.rom file generated for
qboot.  I see SeaBIOS take about 20ms on my old AMD system (again,
further optimizations could be made with some simple code changes).  I
used the following qemu command line:

qemu-system-x86_64 -drive if=pflash,file=../seabios/out/bios.bin,readonly=on -drive if=pflash,file=cbfs.rom,readonly=on -enable-kvm -serial mon:stdio

As a side note, if there is real interest in placing a kernel in a
"flash" like memory mapped device, it would be nice to do something
like the CBFS simple-elf (SELF) format so that the firmware doesn't
have to parse the bzimage.

-Kevin


diff --git a/src/Kconfig b/src/Kconfig
index 14c38fb..7484782 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -74,7 +74,7 @@ endchoice
             "bootorder" file.
 
     config COREBOOT_FLASH
-        depends on COREBOOT
+        #depends on COREBOOT
         bool "coreboot CBFS support"
         default y
         help
diff --git a/src/fw/coreboot.c b/src/fw/coreboot.c
index 8fd8449..3af67a8 100644
--- a/src/fw/coreboot.c
+++ b/src/fw/coreboot.c
@@ -421,6 +421,9 @@ coreboot_cbfs_init(void)
         return;
 
     struct cbfs_header *hdr = *(void **)(CONFIG_CBFS_LOCATION - 4);
+    if (CBFS_HEADER_MAGIC && (u32)hdr > CONFIG_CBFS_LOCATION)
+        // Looks like the pointer is relative to CONFIG_CBFS_LOCATION
+        hdr = (void*)hdr + CONFIG_CBFS_LOCATION;
     if (hdr->magic != cpu_to_be32(CBFS_HEADER_MAGIC)) {
         dprintf(1, "Unable to find CBFS (ptr=%p; got %x not %x)\n"
                 , hdr, hdr->magic, cpu_to_be32(CBFS_HEADER_MAGIC));
@@ -464,6 +467,73 @@ coreboot_cbfs_init(void)
     process_links_file();
 }
 
+#include "bregs.h"
+struct cbfs_file *File_cmdline, *File_initrd;
+
+void
+cbfs_run_vmlinuz(struct cbfs_file *fhdr)
+{
+    dprintf(1, "Loading kernel\n");
+    void *kernel_src = (void*)fhdr + be32_to_cpu(fhdr->offset);
+    u32 kernel_size = be32_to_cpu(fhdr->len);
+    if (readl(kernel_src+0x202) != 0x53726448) {
+        dprintf(1, "Not valid kernel\n");
+        return;
+    }
+    u16 protocol = readw(kernel_src+0x206);
+    if (protocol < 0x203) {
+        dprintf(1, "Old kernel (v %x) not supported\n", protocol);
+        return;
+    }
+    u32 cmdline_size = 0, initrd_size = 0;
+    void *initrd_src, *cmdline_src;
+    if (File_cmdline) {
+        cmdline_size = be32_to_cpu(File_cmdline->len);
+        cmdline_src = (void*)File_cmdline + be32_to_cpu(File_cmdline->offset);
+    }
+    if (File_initrd) {
+        initrd_size = be32_to_cpu(File_initrd->len);
+        initrd_src = (void*)File_initrd + be32_to_cpu(File_initrd->offset);
+    }
+    u32 real_addr=0x10000, cmdline_addr=0x20000;
+    u32 prot_addr=0x100000, initrd_addr=0;
+    if (initrd_size) {
+        u32 initrd_max = readl(kernel_src+0x22c);
+        if (initrd_max > LegacyRamSize - 1)
+            initrd_max = LegacyRamSize - 1;
+        initrd_addr = ALIGN_DOWN(initrd_max - initrd_size, 4096);
+    }
+    u32 setup_size = (readb(kernel_src+0x1f1) + 1) * 512;
+    if (setup_size <= 512)
+        setup_size = 5 * 512;
+    kernel_size -= setup_size;
+
+    // Copy data
+    memcpy((void*)real_addr, (void*)kernel_src, setup_size);
+    memcpy((void*)prot_addr, (void*)kernel_src + setup_size, kernel_size);
+    if (initrd_size)
+        memcpy((void*)initrd_addr, (void*)initrd_src, initrd_size);
+    if (cmdline_size)
+        memcpy((void*)cmdline_addr, (void*)cmdline_src, cmdline_size);
+    writel((void*)real_addr+0x228, cmdline_addr);
+    writeb((void*)real_addr+0x210, 0xB0);
+    writeb((void*)real_addr+0x211, readb((void*)real_addr+0x211) | 0x80);
+    writew((void*)real_addr+0x224, cmdline_addr-real_addr-0x200);
+    writel((void*)real_addr+0x218, initrd_addr);
+    writel((void*)real_addr+0x21c, initrd_size);
+
+    dprintf(1, "Jumping to kernel %d@%x %d@%x %d@%x %d@%x\n"
+            , setup_size, real_addr, cmdline_size, cmdline_addr
+            , kernel_size, prot_addr, initrd_size, initrd_addr);
+    struct bregs br;
+    memset(&br, 0, sizeof(br));
+    extern void kernel_stub(void);
+    br.ebx = real_addr >> 4;
+    br.edx = cmdline_addr - real_addr - 16;
+    br.code = SEGOFF(SEG_BIOS, (u32)kernel_stub - BUILD_BIOS_ADDR);
+    farcall16big(&br);
+}
+
 struct cbfs_payload_segment {
     u32 type;
     u32 compression;
@@ -489,6 +559,10 @@ cbfs_run_payload(struct cbfs_file *fhdr)
     if (!CONFIG_COREBOOT_FLASH || !fhdr)
         return;
     dprintf(1, "Run %s\n", fhdr->filename);
+    if (strcmp(fhdr->filename, "vmlinuz") == 0) {
+        cbfs_run_vmlinuz(fhdr);
+        return;
+    }
     struct cbfs_payload *pay = (void*)fhdr + be32_to_cpu(fhdr->offset);
     struct cbfs_payload_segment *seg = pay->segments;
     for (;;) {
@@ -550,4 +624,19 @@ cbfs_payload_setup(void)
         char *desc = znprintf(MAXDESCSIZE, "Payload [%s]", &filename[4]);
         boot_add_cbfs(cfile->fhdr, desc, bootprio_find_named_rom(filename, 0));
     }
+
+    file = romfile_find("vmlinuz");
+    if (file) {
+        struct cbfs_romfile_s *cfile;
+        cfile = container_of(file, struct cbfs_romfile_s, file);
+        char *desc = znprintf(MAXDESCSIZE, "vmlinuz");
+        boot_add_cbfs(cfile->fhdr, desc, bootprio_find_named_rom("vmlinuz", 0));
+
+        file = romfile_find("cmdline");
+        if (file)
+            File_cmdline = container_of(file, struct cbfs_romfile_s, file)->fhdr;
+        file = romfile_find("initrd");
+        if (file)
+            File_initrd = container_of(file, struct cbfs_romfile_s, file)->fhdr;
+    }
 }
diff --git a/src/romlayout.S b/src/romlayout.S
index 7938e22..f98b820 100644
--- a/src/romlayout.S
+++ b/src/romlayout.S
@@ -197,6 +197,27 @@ __farcall16:
         IRQ_TRAMPOLINE 4a
 
 
+        DECLFUNC kernel_stub
+kernel_stub:
+        movw %bx, %ds
+        movw %bx, %es
+        movw %bx, %fs
+        movw %bx, %gs
+        movw %bx, %ss
+        movl %edx, %esp
+        addw $0x20, %bx
+        pushw %bx    // push CS
+        pushw $0     // push IP
+        xorl %eax, %eax
+        xorl %ebx, %ebx
+        xorl %ecx, %ecx
+        xorl %edx, %edx
+        xorl %edi, %edi
+        xorl %esi, %esi
+        xorl %ebp, %ebp
+        lretw
+
+
 /****************************************************************
  * Misc. entry points.
  ****************************************************************/
-------------- next part --------------
#
# Automatically generated file; DO NOT EDIT.
# SeaBIOS Configuration
#

#
# General Features
#
# CONFIG_COREBOOT is not set
CONFIG_QEMU=y
# CONFIG_CSM is not set
CONFIG_QEMU_HARDWARE=y
# CONFIG_XEN is not set
# CONFIG_THREADS is not set
# CONFIG_RELOCATE_INIT is not set
# CONFIG_BOOTMENU is not set
# CONFIG_BOOTORDER is not set
CONFIG_COREBOOT_FLASH=y
# CONFIG_LZMA is not set
CONFIG_CBFS_LOCATION=0xffff0000
# CONFIG_FLASH_FLOPPY is not set
CONFIG_ENTRY_EXTRASTACK=y
CONFIG_MALLOC_UPPERMEMORY=y
CONFIG_ROM_SIZE=64

#
# Hardware support
#
# CONFIG_ATA is not set
# CONFIG_AHCI is not set
# CONFIG_SDCARD is not set
# CONFIG_VIRTIO_BLK is not set
# CONFIG_VIRTIO_SCSI is not set
# CONFIG_PVSCSI is not set
# CONFIG_ESP_SCSI is not set
# CONFIG_LSI_SCSI is not set
# CONFIG_MEGASAS is not set
# CONFIG_FLOPPY is not set
# CONFIG_PS2PORT is not set
# CONFIG_USB is not set
# CONFIG_SERIAL is not set
# CONFIG_LPT is not set
# CONFIG_USE_SMM is not set
CONFIG_MTRR_INIT=y
CONFIG_PMTIMER=y

#
# BIOS interfaces
#
CONFIG_DRIVES=y
CONFIG_CDROM_BOOT=y
CONFIG_CDROM_EMU=y
CONFIG_PCIBIOS=y
CONFIG_APMBIOS=y
CONFIG_PNPBIOS=y
# CONFIG_OPTIONROMS is not set
CONFIG_BOOT=y
CONFIG_KEYBOARD=y
CONFIG_KBD_CALL_INT15_4F=y
CONFIG_MOUSE=y
CONFIG_S3_RESUME=y
CONFIG_VGAHOOKS=y
# CONFIG_DISABLE_A20 is not set
CONFIG_WRITABLE_UPPERMEMORY=y
# CONFIG_TCGBIOS is not set

#
# BIOS Tables
#
# CONFIG_PIRTABLE is not set
# CONFIG_MPTABLE is not set
# CONFIG_SMBIOS is not set
# CONFIG_ACPI is not set
# CONFIG_FW_ROMFILE_LOAD is not set

#
# VGA ROM
#
CONFIG_NO_VGABIOS=y
# CONFIG_VGA_STANDARD_VGA is not set
# CONFIG_VGA_CIRRUS is not set
# CONFIG_VGA_BOCHS is not set
# CONFIG_VGA_GEODEGX2 is not set
# CONFIG_VGA_GEODELX is not set
# CONFIG_BUILD_VGABIOS is not set
CONFIG_VGA_EXTRA_STACK_SIZE=512

#
# Debugging
#
CONFIG_DEBUG_LEVEL=0


More information about the SeaBIOS mailing list