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

Marc Marí markmb at redhat.com
Mon Aug 31 09:12:02 CET 2015


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));
+    }
 }
 
 
@@ -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
 #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);
+}
 
 /****************************************************************
  * 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);
+
+    dprintf(1, "Jumping to kernel %d@%x %d@%x %d@%x %d@%x\n"
+            , setup_size, (u32)setup_addr, cmdline_size, (u32)cmdline_addr
+            , kernel_size, (u32)kernel_addr, initrd_size, (u32)initrd_addr);
+    struct bregs br;
+    memset(&br, 0, sizeof(br));
+    extern void kernel_stub(void);
+    br.ebx = (u32)setup_addr >> 4;
+    br.edx = (u32)cmdline_addr - (u32)setup_addr - 16;
+    br.code = SEGOFF(SEG_BIOS, (u32)kernel_stub - BUILD_BIOS_ADDR);
+
+    farcall16big(&br);
+}
diff --git a/src/fw/paravirt.h b/src/fw/paravirt.h
index 5e5c61e..a9674c0 100644
--- a/src/fw/paravirt.h
+++ b/src/fw/paravirt.h
@@ -46,9 +46,44 @@ static inline int runningOnKVM(void) {
 // QEMU_CFG_DMA ID bit
 #define QEMU_CFG_VERSION_DMA    2
 
+// 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_RAM_SIZE               0x03
+#define QEMU_CFG_NOGRAPHIC              0x04
+#define QEMU_CFG_NB_CPUS                0x05
+#define QEMU_CFG_MACHINE_ID             0x06
+#define QEMU_CFG_KERNEL_ADDR            0x07
+#define QEMU_CFG_KERNEL_SIZE            0x08
+#define QEMU_CFG_KERNEL_CMDLINE         0x09
+#define QEMU_CFG_INITRD_ADDR            0x0a
+#define QEMU_CFG_INITRD_SIZE            0x0b
+#define QEMU_CFG_BOOT_DEVICE            0x0c
+#define QEMU_CFG_NUMA                   0x0d
+#define QEMU_CFG_BOOT_MENU              0x0e
+#define QEMU_CFG_MAX_CPUS               0x0f
+#define QEMU_CFG_KERNEL_ENTRY           0x10
+#define QEMU_CFG_KERNEL_DATA            0x11
+#define QEMU_CFG_INITRD_DATA            0x12
+#define QEMU_CFG_CMDLINE_ADDR           0x13
+#define QEMU_CFG_CMDLINE_SIZE           0x14
+#define QEMU_CFG_CMDLINE_DATA           0x15
+#define QEMU_CFG_SETUP_ADDR             0x16
+#define QEMU_CFG_SETUP_SIZE             0x17
+#define QEMU_CFG_SETUP_DATA             0x18
+#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)
+
 int qemu_cfg_dma_enabled(void);
 void qemu_preinit(void);
 void qemu_platform_setup(void);
 void qemu_cfg_init(void);
+void qemu_cfg_dma_boot_linux(void);
 
 #endif
diff --git a/src/romlayout.S b/src/romlayout.S
index 7938e22..1c641c2 100644
--- a/src/romlayout.S
+++ b/src/romlayout.S
@@ -196,6 +196,26 @@ __farcall16:
         IRQ_TRAMPOLINE 1c
         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.
diff --git a/src/util.h b/src/util.h
index 327abeb..f99cdb5 100644
--- a/src/util.h
+++ b/src/util.h
@@ -25,6 +25,7 @@ void boot_add_floppy(struct drive_s *drive_g, const char *desc, int prio);
 void boot_add_hd(struct drive_s *drive_g, const char *desc, int prio);
 void boot_add_cd(struct drive_s *drive_g, const char *desc, int prio);
 void boot_add_cbfs(void *data, const char *desc, int prio);
+void boot_add_dma(const char *desc, int prio);
 void interactive_bootmenu(void);
 void bcv_prepboot(void);
 struct pci_device;
-- 
2.4.3




More information about the SeaBIOS mailing list