[SeaBIOS] [PATCH v4 2/2] Boot Linux using QEMU fw_cfg DMA interface
Marc Marí
markmb at redhat.com
Thu Oct 1 12:16:06 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 | 40 +++++++++++++++++++++++-----
src/fw/paravirt.c | 80 ++++++++++++++++++++++++++++++++++++++++++++-----------
src/fw/paravirt.h | 36 +++++++++++++++++++++++++
src/post.c | 1 +
src/romlayout.S | 20 ++++++++++++++
src/util.h | 2 ++
6 files changed, 156 insertions(+), 23 deletions(-)
diff --git a/src/boot.c b/src/boot.c
index e0f73a3..305b10a 100644
--- a/src/boot.c
+++ b/src/boot.c
@@ -235,6 +235,14 @@ int bootprio_find_usb(struct usbdevice_s *usbdev, int lun)
return find_prio(desc);
}
+int bootprio_find_vmlinux(void)
+{
+ if (!CONFIG_BOOTORDER) {
+ return -1;
+ }
+ return find_prio("vmlinux");
+}
+
/****************************************************************
* Boot setup
@@ -300,13 +308,14 @@ struct bootentry_s {
};
static struct hlist_head BootList VARVERIFY32INIT;
-#define IPL_TYPE_FLOPPY 0x01
-#define IPL_TYPE_HARDDISK 0x02
-#define IPL_TYPE_CDROM 0x03
-#define IPL_TYPE_CBFS 0x20
-#define IPL_TYPE_BEV 0x80
-#define IPL_TYPE_BCV 0x81
-#define IPL_TYPE_HALT 0xf0
+#define IPL_TYPE_FLOPPY 0x01
+#define IPL_TYPE_HARDDISK 0x02
+#define IPL_TYPE_CDROM 0x03
+#define IPL_TYPE_CBFS 0x20
+#define IPL_TYPE_QEMU_VMLINUX 0x21
+#define IPL_TYPE_BEV 0x80
+#define IPL_TYPE_BCV 0x81
+#define IPL_TYPE_HALT 0xf0
static void
bootentry_add(int type, int prio, u32 data, const char *desc)
@@ -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_qemu_vmlinux(const char *desc, int prio)
+{
+ bootentry_add(IPL_TYPE_QEMU_VMLINUX, 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_qemu_vmlinux_dma(void)
+{
+ printf("Booting Linux from fw_cfg...\n");
+ qemu_vmlinux_boot();
+}
+
// 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_QEMU_VMLINUX:
+ boot_qemu_vmlinux_dma();
+ break;
case IPL_TYPE_HALT:
boot_fail();
break;
diff --git a/src/fw/paravirt.c b/src/fw/paravirt.c
index 65f9fba..d3102a9 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)
{
@@ -509,3 +495,65 @@ void qemu_cfg_init(void)
dprintf(1, "Moving pm_base to 0x%x\n", acpi_pm_base);
}
}
+
+void qemu_vmlinux_boot(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);
+
+ u16 protocol = readw(setup_addr + 0x206);
+ if (protocol < 0x203) {
+ /* Assume initrd_max 0x37ffffff */
+ writel(setup_addr + 0x22c, 0x37ffffff);
+ }
+
+ 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);
+
+ u32 initrd_end_page = ALIGN_DOWN((u32)(initrd_addr + initrd_size), 4096);
+ u32 max_allowed_page = ALIGN_DOWN(readl(setup_addr + 0x22c), 4096);
+ if (initrd_end_page != max_allowed_page) {
+ /* Initrd at the end of memory. Compute better initrd address
+ * based on e801 data
+ */
+ initrd_addr = (void *)(ALIGN_DOWN((LegacyRamSize - initrd_size), 4096));
+ writel(setup_addr + 0x218, (u32)initrd_addr);
+ }
+
+ qemu_cfg_read_entry(initrd_addr, QEMU_CFG_INITRD_DATA, initrd_size);
+
+ 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);
+ qemu_cfg_read_entry(cmdline_addr, QEMU_CFG_CMDLINE_DATA, cmdline_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);
+}
+
+void qemu_vmlinux_setup(void) {
+ boot_add_qemu_vmlinux("QEMU Kernel image", bootprio_find_vmlinux());
+}
diff --git a/src/fw/paravirt.h b/src/fw/paravirt.h
index ed8e5f1..59bfb7f 100644
--- a/src/fw/paravirt.h
+++ b/src/fw/paravirt.h
@@ -47,9 +47,45 @@ 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_vmlinux_boot(void);
+void qemu_vmlinux_setup(void);
#endif
diff --git a/src/post.c b/src/post.c
index 6803585..73a655a 100644
--- a/src/post.c
+++ b/src/post.c
@@ -153,6 +153,7 @@ device_hardware_setup(void)
esp_scsi_setup();
megasas_setup();
pvscsi_setup();
+ qemu_vmlinux_setup();
}
static void
diff --git a/src/romlayout.S b/src/romlayout.S
index fefc212..d1264b8 100644
--- a/src/romlayout.S
+++ b/src/romlayout.S
@@ -179,6 +179,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..2b521b8 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_qemu_vmlinux(const char *desc, int prio);
void interactive_bootmenu(void);
void bcv_prepboot(void);
struct pci_device;
@@ -36,6 +37,7 @@ int bootprio_find_pci_rom(struct pci_device *pci, int instance);
int bootprio_find_named_rom(const char *name, int instance);
struct usbdevice_s;
int bootprio_find_usb(struct usbdevice_s *usbdev, int lun);
+int bootprio_find_vmlinux(void);
// bootsplash.c
void enable_vga_console(void);
--
2.4.3
More information about the SeaBIOS
mailing list