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

Marc Marí markmb at redhat.com
Mon Aug 31 17:10:37 CET 2015


On Mon, 31 Aug 2015 12:46:57 -0400
"Kevin O'Connor" <kevin at koconnor.net> wrote:

> 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.

In QEMU, this is done in the linuxboot image, the one that I'm
overloading. But this image is written in assembly. And using the DMA
interface in assembly can get really tricky with all this memory
management. That's why I rewrote the boot in C here.

Thanks
Marc



More information about the SeaBIOS mailing list