It's probably not good to override all the ROM options. Better ways can be discussed.
Signed-off-by: Marc MarĂ markmb@redhat.com --- src/boot.c | 14 +++++++++++- src/fw/paravirt.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/fw/paravirt.h | 1 + src/romlayout.S | 20 ++++++++++++++++ 4 files changed, 102 insertions(+), 1 deletion(-)
diff --git a/src/boot.c b/src/boot.c index e0f73a3..7d187c5 100644 --- a/src/boot.c +++ b/src/boot.c @@ -684,6 +684,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) @@ -732,7 +740,11 @@ do_boot(int seq_nr) boot_cbfs((void*)ie->vector); break; case IPL_TYPE_BEV: - boot_rom(ie->vector); + if (qemu_cfg_dma_enabled()) { + boot_linux_cfg_dma(); + } else { + boot_rom(ie->vector); + } break; case IPL_TYPE_HALT: boot_fail(); diff --git a/src/fw/paravirt.c b/src/fw/paravirt.c index b7ab169..ada8574 100644 --- a/src/fw/paravirt.c +++ b/src/fw/paravirt.c @@ -23,6 +23,8 @@ #include "util.h" // pci_setup #include "x86.h" // cpuid #include "xen.h" // xen_biostable_setup +#include "bregs.h" // struct bregs +#include "stacks.h" // farcall16big
// Amount of continuous ram under 4Gig u32 RamSize; @@ -554,3 +556,69 @@ 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, 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, 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); + + outb(0xa, 0xf4); + + farcall16big(&br); +} diff --git a/src/fw/paravirt.h b/src/fw/paravirt.h index a5fe913..c6a97c4 100644 --- a/src/fw/paravirt.h +++ b/src/fw/paravirt.h @@ -82,5 +82,6 @@ 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();
#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.