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@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();
case IPL_TYPE_HALT: boot_fail(); break;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.
-Kevin