[SeaBIOS] [PATCH v2 2/2] Boot Linux using QEMU fw_cfg DMA interface
Marc Marí
markmb at redhat.com
Mon Aug 31 18:21:39 CET 2015
On Mon, 31 Aug 2015 14:07:28 -0400
"Kevin O'Connor" <kevin at koconnor.net> wrote:
> On Mon, Aug 31, 2015 at 07:10:37PM +0200, Marc Marí wrote:
> > 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.
>
> Yes, and that makes sense.
>
> However, the checks in the new qemu_cfg_dma_boot_linux() do not match
> the checks in QEMU's pc-bios/optionrom/linuxboot.S . So, which is
> right?
>
I suppose linuxboot.S is right (because it was already working). I'll
check what checks are missing and add them (if appropiate).
Marc
More information about the SeaBIOS
mailing list