[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