[SeaBIOS] [PATCH v2 2/2] Boot Linux using QEMU fw_cfg DMA interface

Kevin O'Connor kevin at koconnor.net
Mon Aug 31 16:46:57 CET 2015


On Mon, Aug 31, 2015 at 11:12:02AM +0200, Marc Marí wrote:
> Reading Linux from the fw_cfg interface is faster using the DMA interface.
> For this reason, add a Linux loader that can benefit from this interface.
> 
> Signed-off-by: Marc Marí <markmb at redhat.com>
> ---
>  src/boot.c        | 26 +++++++++++++++++
>  src/fw/paravirt.c | 84 ++++++++++++++++++++++++++++++++++++++++++++-----------
>  src/fw/paravirt.h | 35 +++++++++++++++++++++++
>  src/romlayout.S   | 20 +++++++++++++
>  src/util.h        |  1 +
>  5 files changed, 150 insertions(+), 16 deletions(-)
> 
> diff --git a/src/boot.c b/src/boot.c
> index e0f73a3..ba692db 100644
> --- a/src/boot.c
> +++ b/src/boot.c
> @@ -280,6 +280,14 @@ boot_init(void)
>      BootRetryTime = romfile_loadint("etc/boot-fail-wait", 60*1000);
>  
>      loadBootOrder();
> +
> +    /* Check for booting directly from fw_cfg DMA. We assign the same boot
> +     * priority of the linuxboot rom (if it exists).
> +     */
> +    if (qemu_cfg_dma_enabled()) {
> +        boot_add_dma("QEMU Kernel image",
> +                    bootprio_find_named_rom("genroms/linuxboot.bin", 0));
> +    }

boot_init() isn't the place for this - add a new function (eg,
qemu_vmlinux_setup() ) to paravirt.c and call it from
post.c:device_hardware_setup().

Overloading "genroms/linuxboot.bin" is fine for testing, but I think
ultimately a new name (eg, "vmlinux") should be made and QEMU should
be enhanced to pass that new name in the bootorder file.

> @@ -304,6 +312,7 @@ static struct hlist_head BootList VARVERIFY32INIT;
>  #define IPL_TYPE_HARDDISK    0x02
>  #define IPL_TYPE_CDROM       0x03
>  #define IPL_TYPE_CBFS        0x20
> +#define IPL_TYPE_DMA         0x21

How about "IPL_TYPE_QEMU_VMLINUX".

>  #define IPL_TYPE_BEV         0x80
>  #define IPL_TYPE_BCV         0x81
>  #define IPL_TYPE_HALT        0xf0
> @@ -398,6 +407,12 @@ boot_add_cbfs(void *data, const char *desc, int prio)
>      bootentry_add(IPL_TYPE_CBFS, defPrio(prio, DEFAULT_PRIO), (u32)data, desc);
>  }
>  
> +// Add a FW_CFG DMA boot
> +void
> +boot_add_dma(const char *desc, int prio)
> +{
> +    bootentry_add(IPL_TYPE_DMA, defPrio(prio, DEFAULT_PRIO), 0, desc);
> +}

boot_add_qemu_vmlinux()

>  /****************************************************************
>   * Keyboard calls
> @@ -684,6 +699,14 @@ boot_rom(u32 vector)
>      call_boot_entry(so, 0);
>  }
>  
> +// Boot from a linuxboot ROM when QEMU cfg is in DMA mode
> +static void
> +boot_linux_cfg_dma(void)
> +{
> +    printf("Booting Linux from fw_cfg...\n");
> +    qemu_cfg_dma_boot_linux();
> +}
> +
>  // Unable to find bootable device - warn user and eventually retry.
>  static void
>  boot_fail(void)
> @@ -734,6 +757,9 @@ do_boot(int seq_nr)
>      case IPL_TYPE_BEV:
>          boot_rom(ie->vector);
>          break;
> +    case IPL_TYPE_DMA:
> +        boot_linux_cfg_dma();
> +        break;
>      case IPL_TYPE_HALT:
>          boot_fail();
>          break;
> diff --git a/src/fw/paravirt.c b/src/fw/paravirt.c
> index 26af499..797d8ba 100644
> --- a/src/fw/paravirt.c
> +++ b/src/fw/paravirt.c
> @@ -23,7 +23,8 @@
>  #include "util.h" // pci_setup
>  #include "x86.h" // cpuid
>  #include "xen.h" // xen_biostable_setup
> -#include "stacks.h" // yield
> +#include "bregs.h" // struct bregs
> +#include "stacks.h" // farcall16big, yield
>  
>  // Amount of continuous ram under 4Gig
>  u32 RamSize;
> @@ -185,21 +186,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_NUMA                   0x0d
> -#define QEMU_CFG_BOOT_MENU              0x0e
> -#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)
>  {
> @@ -505,3 +491,67 @@ void qemu_cfg_init(void)
>          dprintf(1, "Moving pm_base to 0x%x\n", acpi_pm_base);
>      }
>  }
> +
> +void qemu_cfg_dma_boot_linux(void)
> +{
> +    dprintf(1, "Loading kernel\n");
> +    void *setup_addr;
> +    u32 setup_size;
> +    qemu_cfg_read_entry(&setup_addr, QEMU_CFG_SETUP_ADDR, 4);
> +    qemu_cfg_read_entry(&setup_size, QEMU_CFG_SETUP_SIZE, 4);
> +    qemu_cfg_read_entry(setup_addr, QEMU_CFG_SETUP_DATA, setup_size);
> +
> +    if (readl(setup_addr + 0x202) != 0x53726448) {
> +        dprintf(1, "Not valid kernel\n");
> +        return;
> +    }
> +
> +    u16 protocol = readw(setup_addr + 0x206);
> +    if (protocol < 0x203) {
> +        dprintf(1, "Old kernel (v %x) not supported\n", protocol);
> +        return;
> +    }
> +
> +    void *kernel_addr;
> +    u32 kernel_size;
> +
> +    qemu_cfg_read_entry(&kernel_addr, QEMU_CFG_KERNEL_ADDR, 4);
> +    qemu_cfg_read_entry(&kernel_size, QEMU_CFG_KERNEL_SIZE, 4);
> +    qemu_cfg_read_entry(kernel_addr, QEMU_CFG_KERNEL_DATA, kernel_size);
> +
> +    void *cmdline_addr;
> +    u32 cmdline_size;
> +    qemu_cfg_read_entry(&cmdline_addr, QEMU_CFG_CMDLINE_ADDR, 4);
> +    qemu_cfg_read_entry(&cmdline_size, QEMU_CFG_CMDLINE_SIZE, 4);
> +    if (cmdline_size) {
> +        qemu_cfg_read_entry(cmdline_addr, QEMU_CFG_CMDLINE_DATA, cmdline_size);
> +    }
> +
> +    void *initrd_addr;
> +    u32 initrd_size;
> +    qemu_cfg_read_entry(&initrd_addr, QEMU_CFG_INITRD_ADDR, 4);
> +    qemu_cfg_read_entry(&initrd_size, QEMU_CFG_INITRD_SIZE, 4);
> +    if (initrd_size) {
> +        qemu_cfg_read_entry(initrd_addr, QEMU_CFG_INITRD_DATA, initrd_size);
> +    }
> +
> +    // Last configurations
> +    writel(setup_addr + 0x228, (u32)cmdline_addr);
> +    writeb(setup_addr + 0x210, 0xB0);
> +    writeb(setup_addr + 0x211, readb(setup_addr + 0x211) | 0x80);
> +    writew(setup_addr + 0x224, cmdline_addr - setup_addr - 0x200);
> +    writel(setup_addr + 0x218, (u32)initrd_addr);
> +    writel(setup_addr + 0x21c, initrd_size);

Are these updates necessary when using the existing QEMU vmlinux
fw_cfg entries?  I thought QEMU did this on behalf of the firmware,
but maybe I missed something.

-Kevin



More information about the SeaBIOS mailing list