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@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);